# TokPortal API > TokPortal lets developers create geo-targeted TikTok and Instagram accounts, configure videos, publish bundles, retrieve delivered accounts, and pull analytics through one API. ## Canonical URLs - Docs: https://developers.tokportal.com - App API base URL: https://app.tokportal.com/api/ext - OpenAPI JSON: https://developers.tokportal.com/openapi.json - OpenAPI YAML: https://developers.tokportal.com/openapi.yaml - Full LLM context: https://developers.tokportal.com/llms-full.txt - AI context JSON: https://developers.tokportal.com/ai-context.json - Developer ecosystem JSON: https://developers.tokportal.com/developer-ecosystem.json - Package release manifest: https://developers.tokportal.com/package-release-manifest.json - Developer package export command: `npm run export:developer-packages`. ## API Versioning - Current public API version: `2026-05-25` - Response header: `X-TokPortal-API-Version: 2026-05-25` - Response header: `X-TokPortal-API-Stability: stable` - Response header: `X-TokPortal-Request-ID: req_...` - Rate limit headers on authenticated responses: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`. - 429 responses include `Retry-After`. - Compatibility policy: additive non-breaking changes remain in the current version; breaking changes require a new public API version and changelog entry. ## Authentication - Header: `X-API-Key: sk_...` - New keys use `sk_` followed by 64 lowercase hex characters. - Raw keys are shown once at creation and stored by TokPortal as SHA-256 hashes. ## Core Concepts - Profiles: API key owner and developer account context. - Bundles: account/video work orders grouped by platform and country. - Accounts: delivered social accounts with credentials and verification flows. - Posts: video slots configured, published, reviewed, and finalized inside bundles. ## API Reference - [Social Media Account Creation API – Overview](https://developers.tokportal.com/overview) - [API Authentication & API Keys](https://developers.tokportal.com/authentication) - [API Credits & Pricing Per Operation](https://developers.tokportal.com/credits) - [API Error Codes & Troubleshooting](https://developers.tokportal.com/errors) - [Rate Limits, Pagination & Idempotency](https://developers.tokportal.com/rate-limits) - [Getting Started – First API Call in 5 Minutes](https://developers.tokportal.com/getting-started) - [OpenAPI, SDKs, CLI & MCP](https://developers.tokportal.com/sdks-cli) - [AI Context](https://developers.tokportal.com/ai-context) - [Versioning & Stability](https://developers.tokportal.com/versioning) - [Changelog](https://developers.tokportal.com/changelog) - [Bundles – Campaign Management API](https://developers.tokportal.com/bundles) - [Create Bundle - Provision Accounts & Videos](https://developers.tokportal.com/create-bundle) - [Create Bulk - Multiple Bundles](https://developers.tokportal.com/create-bulk) - [Publish & Unpublish Bundles via API](https://developers.tokportal.com/publish-unpublish) - [Account Configuration – Profile Setup API](https://developers.tokportal.com/account-configuration) - [Account Review & Approval Actions](https://developers.tokportal.com/account-actions) - [Video Management – Types, Formats & Statuses](https://developers.tokportal.com/videos) - [Configure Videos – Metadata & Scheduling API](https://developers.tokportal.com/configure-videos) - [CSV Import – Bulk Video Upload from Spreadsheet](https://developers.tokportal.com/csv-import) - [Video Actions – Publish, Finalize & Corrections](https://developers.tokportal.com/video-actions) - [Fix Broken Video Download Links](https://developers.tokportal.com/fix-download-link) - [Delivered Accounts – Credentials & Verification Codes](https://developers.tokportal.com/saved-accounts) - [Account Edit Request – Profile Updates via API](https://developers.tokportal.com/account-edit-request) - [Analytics API – Analytics v2](https://developers.tokportal.com/analytics) - [Media Upload – Videos & Images via Presigned URLs](https://developers.tokportal.com/media-upload) - [MCP Server – AI Integration for Cursor & Claude](https://developers.tokportal.com/mcp) - [OpenClaw Skill – AI Social Media Automation](https://developers.tokportal.com/openclaw) - [Comments – Overview & Lifecycle](https://developers.tokportal.com/comments-overview) - [Create Comments – Single & Batch](https://developers.tokportal.com/create-comments) - [List & Read Comment Tasks](https://developers.tokportal.com/list-and-read-comments) - [Approve & Dispute – Review Flow](https://developers.tokportal.com/comments-review-flow) - [Verifier Timeline – Verbose Audit](https://developers.tokportal.com/comments-verifications) - [Webhooks](https://developers.tokportal.com/webhooks) - [API Reference](https://developers.tokportal.com/api-reference) ## Use Cases - [TokPortal + n8n - TikTok Workflow via HTTP](https://developers.tokportal.com/use-cases/n8n-tiktok-automation) - [TokPortal + Zapier - Social Campaigns via Webhooks](https://developers.tokportal.com/use-cases/zapier-social-media-automation) - [TokPortal + Make - TikTok Workflow via HTTP](https://developers.tokportal.com/use-cases/make-tiktok-automation) - [TokPortal + OpenClaw](https://developers.tokportal.com/use-cases/openclaw-tiktok-posting) - [TokPortal MCP Server for Cursor](https://developers.tokportal.com/use-cases/cursor-mcp-tiktok) - [TokPortal + Claude via MCP](https://developers.tokportal.com/use-cases/claude-code-social-media) - [Create Instagram Account Bundles with TokPortal API](https://developers.tokportal.com/use-cases/create-instagram-accounts-api) - [Order a US TikTok Workflow with TokPortal API](https://developers.tokportal.com/use-cases/post-us-tiktok-from-abroad) - [Bulk TikTok Bundle Creation API](https://developers.tokportal.com/use-cases/bulk-tiktok-accounts-api) - [Python Quickstart - TokPortal API](https://developers.tokportal.com/use-cases/python-quickstart) - [TokPortal API for Agencies](https://developers.tokportal.com/use-cases/agency-multi-client-management) - [DTC Multi-Market Launch](https://developers.tokportal.com/use-cases/dtc-multi-market-launch) - [Music Promotion on TikTok with TokPortal](https://developers.tokportal.com/use-cases/music-promotion-tiktok) - [Product Seeding Workflows for TikTok](https://developers.tokportal.com/use-cases/tiktok-shop-product-seeding) - [UGC Distribution at Scale](https://developers.tokportal.com/use-cases/ugc-distribution-at-scale) - [TokPortal Use Cases & Integrations](https://developers.tokportal.com/use-cases/) ## Public Operations - GET /me — Get authenticated user (getCurrentUser) - GET /countries — List available countries (listCountries) - GET /platforms — List available platforms (listPlatforms) - GET /credit-costs — Get credit pricing (getCreditCosts) - GET /credits/balance — Get credit balance (getCreditBalance) - GET /credits/history — List credit transactions (listCreditTransactions) - GET /bundles — List bundles (listBundles) - POST /bundles — Create a bundle (createBundle) - POST /bundles/bulk — Create bundles in bulk (createBundlesBulk) - GET /bundles/{id} — Get a bundle (getBundle) - PATCH /bundles/{id} — Update bundle settings (updateBundle) - POST /bundles/{id}/publish — Publish a bundle (publishBundle) - GET /bundles/{id}/publish-readiness — Check bundle publish readiness (getBundlePublishReadiness) - POST /bundles/{id}/unpublish — Unpublish a bundle (unpublishBundle) - POST /bundles/{id}/add-video-slots — Add video slots to a bundle (addVideoSlots) - POST /bundles/{id}/add-edit-slots — Add edit slots to a bundle (addEditSlots) - GET /bundles/{id}/account — Get bundle account configuration (getBundleAccount) - PUT /bundles/{id}/account — Configure bundle account profile (configureBundleAccount) - POST /bundles/{id}/account/corrections — Request account corrections (requestBundleAccountCorrections) - POST /bundles/{id}/account/finalize — Finalize account review (finalizeBundleAccount) - GET /bundles/{id}/videos — List bundle video slots (listBundleVideos) - GET /bundles/{id}/videos/{position} — Get video slot configuration (getBundleVideo) - PUT /bundles/{id}/videos/{position} — Configure a video slot (configureBundleVideo) - PATCH /bundles/{id}/videos/{position} — Patch video references (patchBundleVideo) - PUT /bundles/{id}/videos/batch — Configure video slots in bulk (batchConfigureBundleVideos) - POST /bundles/{id}/videos/publish-all — Publish all configured videos on an active bundle (publishAllBundleVideos) - POST /bundles/{id}/videos/import-csv — Import video slots from CSV (importBundleVideosCsv) - POST /bundles/{id}/videos/{position}/publish — Publish one video slot (publishBundleVideo) - POST /bundles/{id}/videos/{position}/reset — Reset one video slot (resetBundleVideo) - POST /bundles/{id}/videos/{position}/unschedule — Unschedule one video slot (unscheduleBundleVideo) - POST /bundles/{id}/videos/{position}/finalize — Finalize video review (finalizeBundleVideo) - POST /bundles/{id}/videos/{position}/corrections — Request video corrections (requestBundleVideoCorrections) - POST /bundles/{id}/videos/{position}/fix-download — Fix a broken video download (fixBundleVideoDownload) - GET /accounts — List delivered accounts (listAccounts) - GET /accounts/{id} — Get a delivered account (getAccount) - GET /accounts/{id}/bundles — List bundles for a delivered account (listAccountBundles) - POST /accounts/{id}/verification-code — Retrieve latest account verification code (retrieveAccountVerificationCode) - POST /accounts/{id}/reveal-credentials — Reveal delivered account credentials (revealAccountCredentials) - GET /accounts/{id}/analytics/can-refresh — Check analytics refresh availability (canRefreshAccountAnalytics) - POST /accounts/{id}/analytics/refresh — Refresh account analytics (refreshAccountAnalytics) - GET /accounts/{id}/edit-request — Get active account edit request (getAccountEditRequest) - POST /accounts/{id}/edit-request — Request profile edits for a delivered account (createAccountEditRequest) - GET /webhooks/events — List webhook event catalog (listWebhookEvents) - GET /webhooks — List webhook endpoints (listWebhookEndpoints) - POST /webhooks — Create a webhook endpoint (createWebhookEndpoint) - GET /webhooks/{id} — Get a webhook endpoint (getWebhookEndpoint) - PATCH /webhooks/{id} — Update a webhook endpoint (updateWebhookEndpoint) - DELETE /webhooks/{id} — Delete a webhook endpoint (deleteWebhookEndpoint) - GET /webhooks/{id}/deliveries — List webhook deliveries (listWebhookDeliveries) - POST /webhooks/{id}/deliveries/{delivery_id}/retry — Retry a webhook delivery (retryWebhookDelivery) - POST /webhooks/{id}/test — Send a test webhook (testWebhookEndpoint) - POST /upload/video — Create a video upload URL (uploadVideo) - POST /upload/video/direct — Upload a video file directly (uploadVideoDirect) - POST /upload/image — Create an image upload URL (uploadImage) - POST /upload/image/direct — Upload an image file directly (uploadImageDirect) - POST /upload/image/from-url — Import an image from URL (uploadImageFromUrl) - GET /analytics — Get analytics dashboard (getAnalyticsDashboard) - GET /analytics/contract — Get analytics data contract (getAnalyticsContract) - GET /analytics/export/videos — Export analytics videos CSV (exportAnalyticsVideos) - POST /analytics/export/reports — Create analytics web report (createAnalyticsReport) - POST /analytics/export/reports/html — Export analytics report HTML (exportAnalyticsReportHtml) - GET /analytics/series — Get analytics time series (getAnalyticsSeries) - GET /analytics/accounts/{id} — Get account analytics drilldown (getAnalyticsAccount) - POST /analytics/accounts/{id}/refresh — Refresh analytics account (refreshAnalyticsAccount) - GET /analytics/accounts/{id}/raw — List raw account analytics snapshots (listAnalyticsAccountRawSnapshots) - GET /accounts/{id}/analytics — Get account analytics compatibility view (getAccountAnalytics) - GET /accounts/{id}/analytics/videos — List post analytics for an account (listAccountVideoAnalytics) - GET /videos/{id}/analytics — Get single video analytics (getVideoAnalytics) - GET /analytics/comments — Get comment pulse analytics (getCommentPulse) - GET /analytics/accounts/{id}/comments — List comments for an account post (listAnalyticsAccountComments) - GET /analytics/posts/{id}/raw — List raw post analytics snapshots (listAnalyticsPostRawSnapshots) - GET /comments — List comment tasks (listCommentTasks) - POST /comments — Create comment tasks (createCommentTasks) - GET /comments/{id} — Get a comment task (getCommentTask) - DELETE /comments/{id} — Delete a comment task (deleteCommentTask) - POST /comments/{id}/approve — Approve a manually confirmed comment task (approveCommentTask) - POST /comments/{id}/dispute — Dispute a manually confirmed comment task (disputeCommentTask) - GET /comments/{id}/verifications — List comment task verification events (listCommentTaskVerifications) # Full Documentation Corpus ## Social Media Account Creation API – Overview Source: api/01-getting-started/00-overview.md URL: https://developers.tokportal.com/overview # Overview The TokPortal API lets you programmatically create geo-targeted TikTok and Instagram account/video workflows, configure content, publish bundles, receive webhooks, and read analytics. The public API covers the developer workflow documented here. Some dashboard-only or future features may not be exposed yet. ## Base URL All API requests are made to: ``` https://app.tokportal.com/api/ext ``` Current public API version: `2026-05-25`. Every response includes `X-TokPortal-API-Version` and `X-TokPortal-API-Stability`; see [Versioning & Stability](versioning). ## Key Concepts - **Multi-platform** — Manage TikTok and Instagram workflows from a single API. - **Credit-based billing** — Operations consume credits. The same credit balance is shared between the API and the web UI. See [Credits & Pricing](credits) for details. - **Documented public surface** — Create bundles, upload media, configure videos, publish, configure webhooks, and pull analytics through the endpoints in this reference. ## Response Format All responses return JSON. **Success responses** wrap the payload in a `data` key: ```json { "data": { "email": "you@example.com", "credits": 1250 } } ``` **Error responses** return an `error` object with a machine-readable `code`, a human-readable `message`, and optional `details`: ```json { "error": { "code": "INSUFFICIENT_CREDITS", "message": "Not enough credits to complete this operation.", "details": { "required": 50, "available": 12 } } } ``` See [Errors](errors) for the full list of error codes. ## Authentication Include your API key in the `X-API-Key` header on every request. See [Authentication](authentication) for details. ## Quick Example Verify your API key and check your account info with a single call: ```bash curl -X GET https://app.tokportal.com/api/ext/me \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "email": "you@example.com", "credits": 1250, "plan": "pro", "created_at": "2025-09-15T10:30:00Z" } } ``` ## What's Next | Topic | Description | |---|---| | [Authentication](authentication) | API key setup and security best practices | | [Credits & Pricing](credits) | Credit costs per operation and balance endpoints | | [Errors](errors) | Full error code reference | | [Rate Limits & Pagination](rate-limits) | Throttling, pagination, and idempotency | | [Versioning & Stability](versioning) | API version headers, compatibility policy, and deprecations | --- ## API Authentication & API Keys Source: api/01-getting-started/01-authentication.md URL: https://developers.tokportal.com/authentication # Authentication The TokPortal API uses API keys to authenticate requests. Include your key in the `X-API-Key` header on every request. ## API Key Format Keys follow the format: ``` sk_<64 lowercase hexadecimal characters> ``` Example: ``` sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4 ``` ## Generating an API Key 1. Sign in to your TokPortal account. 2. Navigate to **Developer > API Keys** at [app.tokportal.com/developer/api-keys](https://app.tokportal.com/developer/api-keys). 3. Click **Create New Key**. 4. Copy the key immediately — it is shown only once. You can create **multiple keys** per account. This is useful for separating keys across environments (development, staging, production) or across different services. ## Environments, Scopes, and Expiry When you create a key, tag it with an environment: | Environment | Recommended use | |---|---| | `production` | Live servers and partner integrations | | `staging` | Pre-production validation | | `development` | Local development | | `test` | Short-lived experiments and CI smoke tests | Keys also support broad access scopes: | Scope | Allows | |---|---| | `*` | Full access. This is the default for existing keys. | | `read` | `GET` requests only. | | `write` | Mutating requests: `POST`, `PUT`, `PATCH`, and `DELETE`. | Use read-only keys for dashboards and sync jobs that never mutate TokPortal state. Use expiring keys for contractors, temporary scripts, and tests. You can update a key's display name, environment tag, access scope, and expiry date from **Developer > API Keys** without rotating the secret. Rotate when the secret itself may be exposed or when you need a fresh value for deployment. ## Key Storage and Security API keys are **hashed with SHA-256** before being stored. TokPortal never retains the plain-text version of your key. If you lose a key, you must revoke it and generate a new one. ## Request IDs and Usage Logs Every public API response includes `X-TokPortal-Request-ID`. Store this value in your logs and include it when contacting support. TokPortal also records request usage per API key in the Developer Portal. Open **Developer > API Keys**, choose a key, and inspect recent requests, status codes, latency, credits charged, and request IDs. This makes it easier to debug one integration without mixing production, staging, and local traffic. ## API Key Audit Trail TokPortal records key lifecycle events separately from request logs: - key created - key metadata updated - key rotated - rotation replacement key created - key revoked Open **Developer > API Keys**, choose a key, and inspect **Key audit trail** to see when the key changed and which metadata was attached at the time, including environment, scopes, expiry, and rotation links. ## Making Authenticated Requests Pass your API key in the `X-API-Key` header: ```bash curl -X GET https://app.tokportal.com/api/ext/me \ -H "X-API-Key: sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" ``` **Success response:** ```json { "data": { "email": "you@example.com", "credits": 1250 } } ``` **Invalid or missing key:** ```json { "error": { "code": "AUTH_INVALID_KEY", "message": "The provided API key is invalid." } } ``` ## Error Codes | Code | HTTP Status | Description | |---|---|---| | `AUTH_MISSING_KEY` | 401 | No `X-API-Key` header was provided. | | `AUTH_INVALID_KEY` | 401 | The key does not match any account. | | `AUTH_REVOKED_KEY` | 401 | The key has been revoked. Generate a new one. | | `AUTH_EXPIRED_KEY` | 401 | The key has expired. Generate or rotate to a new key. | | `AUTH_FORBIDDEN_SCOPE` | 403 | The key does not include the required `read` or `write` scope. | ## Security Best Practices - **Use environment variables.** Never hard-code API keys in source code. ```bash export TOKPORTAL_API_KEY="sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" ``` ```bash curl -X GET https://app.tokportal.com/api/ext/me \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` - **Never expose keys client-side.** API keys must only be used in server-side code. Never include them in frontend JavaScript, mobile apps, or public repositories. - **Rotate keys regularly.** Create a new key, migrate your services, then revoke the old one. - **Use separate keys per environment.** Keep production and development keys distinct so revoking one does not affect the other. - **Use narrow scopes.** Prefer read-only keys for analytics/reporting jobs and full-access keys only for services that create or update resources. - **Set expiry dates for temporary access.** Expiring keys are useful for tests, agencies, contractors, and short-lived scripts. - **Review the audit trail.** Check key lifecycle events during incident response or before deleting old integrations. - **Revoke compromised keys immediately.** If a key is exposed, revoke it from the developer dashboard and generate a replacement. --- ## API Credits & Pricing Per Operation Source: api/01-getting-started/02-credits.md URL: https://developers.tokportal.com/credits # Credits & Pricing TokPortal uses a **credit-based system** as its unified currency. The same credit balance is shared between the API and the web UI — there is no separate API billing. ## Credit Costs | Operation | Cost | |---|---| | Account creation | 25 credits | | Video slot | 2 credits per video | | Niche warming | 7 credits | | Deep warming (Instagram only) | 40 credits | | Video editing | 3 credits per edit slot | | Comment moderation | 25 credits | | Sound volume control | 1 credit per video | ## How Credits Are Debited - **Bundle creation** — Credits for account creation, video slots, warming, editing, and moderation are calculated server-side and debited atomically when the bundle is created. The client never determines the cost; the server computes the total from the bundle configuration. - **Add video slots** — When you add video slots to an existing bundle, credits are debited immediately. - **Add edit slots** — When you add edit slots to an existing bundle, credits are debited immediately. - **Sound volume control** — 1 credit is debited the **first time** `volume_original_sound` or `volume_added_sound` is set on a video. Subsequent updates to the same fields on the same video are free. - **No refunds on unpublish** — Unpublishing an account does not refund credits. ## Endpoints ### Get Credit Balance ```bash curl -X GET https://app.tokportal.com/api/ext/credits/balance \ -H "X-API-Key: sk_xxx" ``` ```json { "data": { "total_credits": 1250, "expiring_within_7_days": 0, "upcoming_expirations": [ { "amount": 638, "expires_at": "2026-06-08T04:47:51.413+00:00", "source": "farmer_topup" } ], "last_updated": "2026-05-19T22:42:36.885166+00:00" } } ``` | Field | Type | Description | |---|---|---| | `total_credits` | number | Current available credit balance. | | `expiring_within_7_days` | number | Sum of credits that will expire in the next 7 days. | | `upcoming_expirations` | array | Credit lots expiring in the next 30 days (sorted by `expires_at` ascending). Each entry has `amount`, `expires_at` (ISO-8601), and `source`. | | `last_updated` | string \| null | Timestamp of the last balance update. | ### Get Credit History ```bash curl -X GET https://app.tokportal.com/api/ext/credits/history?page=1&per_page=10 \ -H "X-API-Key: sk_xxx" ``` ```json { "data": [ { "id": "txn_abc123", "type": "debit", "amount": -52, "description": "Bundle created: 1 TikTok account, 10 videos, niche warming", "breakdown": { "account_creation": 25, "video_slots": 20, "niche_warming": 7 }, "balance_after": 1198, "created_at": "2025-11-20T14:30:00Z" } ], "pagination": { "page": 1, "per_page": 10, "total": 47, "total_pages": 5 } } ``` ### Get Credit Costs Retrieve the current cost table programmatically: ```bash curl -X GET https://app.tokportal.com/api/ext/credit-costs \ -H "X-API-Key: sk_xxx" ``` ```json { "data": { "account_creation": 25, "video_slot": 2, "niche_warming": 7, "deep_warming": 40, "video_editing": 3, "comment_moderation": 25, "sound_volume": 1 } } ``` ## Cost Breakdown Example Creating a bundle with the following configuration: - 1 TikTok account - 10 video slots - Niche warming enabled - Video editing enabled (10 edit slots) | Item | Calculation | Cost | |---|---|---| | Account creation | 1 x 25 | 25 credits | | Video slots | 10 x 2 | 20 credits | | Niche warming | 1 x 7 | 7 credits | | Video editing | 10 x 3 | 30 credits | | **Total** | | **82 credits** | If your balance is below the required total, the API returns an `INSUFFICIENT_CREDITS` error with full details: ```json { "error": { "code": "INSUFFICIENT_CREDITS", "message": "Not enough credits to complete this operation.", "details": { "required": 82, "available": 50, "missing": 32, "breakdown": { "account_creation": 25, "video_slots": 20, "niche_warming": 7, "video_editing": 30 } } } } ``` --- ## API Error Codes & Troubleshooting Source: api/01-getting-started/03-errors.md URL: https://developers.tokportal.com/errors # Errors The TokPortal API uses a consistent error format across all endpoints. Errors are returned as JSON with an `error` object. ## Error Response Format ```json { "error": { "code": "ERROR_CODE", "message": "A human-readable description of the problem.", "details": {} } } ``` | Field | Type | Description | |---|---|---| | `code` | `string` | Machine-readable error code. Use this for programmatic handling. | | `message` | `string` | Human-readable description. May change — do not match against this. | | `details` | `object` | Optional. Additional context (e.g., which field failed validation, credit breakdown). | Every response also includes a correlation header: ```http X-TokPortal-Request-ID: req_... ``` Generated SDKs expose this value on structured API errors as `requestId`, `request_id`, or `RequestID`. Include it when contacting support about a failed request. ## Error Code Reference ### Authentication Errors | Code | HTTP | Description | |---|---|---| | `AUTH_MISSING_KEY` | 401 | No `X-API-Key` header was provided in the request. | | `AUTH_INVALID_KEY` | 401 | The API key does not match any account. | | `AUTH_REVOKED_KEY` | 401 | The API key has been revoked. Generate a new key from the developer dashboard. | ### Rate Limiting | Code | HTTP | Description | |---|---|---| | `RATE_LIMIT_EXCEEDED` | 429 | Too many requests. Wait and retry. See [Rate Limits](rate-limits). | 429 responses include `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. ### Billing | Code | HTTP | Description | |---|---|---| | `INSUFFICIENT_CREDITS` | 402 | Not enough credits. Response includes `required`, `available`, `missing`, and `breakdown` in `details`. | ### Bundle Errors | Code | HTTP | Description | |---|---|---| | `BUNDLE_NOT_FOUND` | 404 | The specified bundle does not exist. | | `BUNDLE_NOT_OWNED` | 403 | The bundle belongs to another account. | | `BUNDLE_INVALID_STATUS` | 409 | The bundle is not in a valid status for this operation (e.g., trying to configure a published bundle). | | `BUNDLE_ALREADY_PUBLISHED` | 409 | The bundle has already been published and cannot be modified. | ### Account Errors | Code | HTTP | Description | |---|---|---| | `ACCOUNT_NOT_FOUND` | 404 | The specified account does not exist. | | `ACCOUNT_INVALID_STATUS` | 409 | The account is not in a valid status for this operation. | | `ACCOUNT_NOT_CONFIGURED` | 409 | The account must be configured before this operation can proceed. | ### Saved Account Errors | Code | HTTP | Description | |---|---|---| | `SAVED_ACCOUNT_NOT_FOUND` | 404 | The specified saved account does not exist. | | `SAVED_ACCOUNT_NOT_OWNED` | 403 | The saved account belongs to another user. | ### Video Errors | Code | HTTP | Description | |---|---|---| | `VIDEO_NOT_FOUND` | 404 | The specified video does not exist. | | `VIDEO_INVALID_STATUS` | 409 | The video is not in a valid status for this operation. | | `VIDEO_POSITION_OUT_OF_RANGE` | 400 | The video position exceeds the available slots. | | `VIDEO_NO_DOWNLOAD_ISSUE` | 409 | The video does not have a download issue to fix. | ### Account Edit Request Errors | Code | HTTP | Description | |---|---|---| | `EDIT_REQUEST_NO_CM` | 400 | No manager assigned to this account. | | `EDIT_REQUEST_ALREADY_EXISTS` | 409 | Another edit request is already in progress. | | `EDIT_REQUEST_NO_ACTIVE_ORDER` | 400 | No active order for this account. | ### Validation Errors | Code | HTTP | Description | |---|---|---| | `INVALID_COUNTRY` | 400 | The country code is not supported. | | `INVALID_PLATFORM` | 400 | The platform is not supported. Valid values: `tiktok`, `instagram`, `youtube`. | | `INVALID_VIDEO_TYPE` | 400 | The video type is not valid for the target platform. | | `INVALID_DATE` | 400 | The date is malformed or out of the allowed range. | | `INVALID_FIELD` | 400 | A field value is invalid (details specify which field and why). | | `INVALID_BODY` | 400 | The request body is malformed or not valid JSON. | | `MISSING_FIELD` | 400 | A required field is missing from the request body. | ### Configuration Errors | Code | HTTP | Description | |---|---|---| | `EDIT_SLOTS_EXCEEDED` | 400 | The number of edit slots exceeds the allowed limit. | | `WARMING_CONFLICT` | 400 | Conflicting warming options were specified. | | `DEEP_WARMING_PLATFORM` | 400 | Deep warming is only available for Instagram accounts. | | `VIDEOS_ONLY_REQUIRES_ACCOUNT` | 400 | A videos-only bundle requires an existing saved account. | | `ACCOUNT_ID_NOT_ALLOWED` | 400 | The `account_id` field was passed with a `bundle_type` other than `videos_only`. To add videos to an existing saved account, set `bundle_type` to `videos_only` (this also skips the account-creation credit charge). | ### Analytics Errors | Code | HTTP | Description | |---|---|---| | `ANALYTICS_COOLDOWN` | 429 | Analytics were recently refreshed. Wait before requesting again. | | `ANALYTICS_QUOTA_EXCEEDED` | 429 | Analytics refresh quota has been exceeded for this period. | | `ANALYTICS_NOT_FOUND` | 404 | No analytics data is available for this account. | ### Verification Errors | Code | HTTP | Description | |---|---|---| | `VERIFICATION_CODE_NOT_FOUND` | 404 | No pending verification code was found for this account. | ### CSV Errors | Code | HTTP | Description | |---|---|---| | `CSV_PARSE_ERROR` | 400 | The CSV file could not be parsed. Check formatting and encoding. | | `CSV_VALIDATION_ERROR` | 400 | The CSV content failed validation. `details` includes row-level errors. | ### Server Errors | Code | HTTP | Description | |---|---|---| | `UPLOAD_FAILED` | 500 | A file upload failed. Retry the request. | | `INTERNAL_ERROR` | 500 | An unexpected server error occurred. If this persists, contact support. | ## Handling Errors in Code ```typescript const response = await fetch("https://app.tokportal.com/api/ext/bundles", { method: "POST", headers: { "X-API-Key": process.env.TOKPORTAL_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify(bundleConfig), }); const result = await response.json(); if (result.error) { switch (result.error.code) { case "INSUFFICIENT_CREDITS": console.error( `Need ${result.error.details.missing} more credits.` ); break; case "AUTH_INVALID_KEY": console.error("Invalid API key. Check your configuration."); break; case "RATE_LIMIT_EXCEEDED": // Wait and retry await new Promise((r) => setTimeout(r, 5000)); break; default: console.error(`API error: ${result.error.message}`); } } ``` --- ## Rate Limits, Pagination & Idempotency Source: api/01-getting-started/04-rate-limits.md URL: https://developers.tokportal.com/rate-limits # Rate Limits & Pagination ## Rate Limits The TokPortal API allows **120 requests per minute per API key**. The limit uses a **token bucket** algorithm that refills continuously, so you do not need to wait for a fixed window to reset. Authenticated responses include rate limit headers: ```http X-RateLimit-Limit: 120 X-RateLimit-Remaining: 119 X-RateLimit-Reset: 1779724800 ``` `X-RateLimit-Reset` is a Unix timestamp estimating when the token bucket will be full again. ### Rate Limit Response When you exceed the limit, the API returns a `429 Too Many Requests` response: ```http HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 120 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1779724800 Retry-After: 1 ``` ```json { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests. Please slow down.", "details": { "retry_after_seconds": 2 } } } ``` ### Best Practices - **Read `Retry-After` first** on 429 responses. It is the canonical wait time before retrying. - **Check `retry_after_seconds`** in the error response and wait before retrying. - **Track `X-RateLimit-Remaining`** to smooth bursts before you hit the limit. - **Use exponential backoff** — if you receive consecutive 429s, increase the wait time between retries. - **Spread requests evenly** rather than sending them in bursts. The token bucket refills at approximately 2 tokens per second, so a steady rate of 2 req/s is sustainable. - **Use separate API keys** for independent services to isolate rate limits. ## Pagination All list endpoints support pagination via query parameters. ### Query Parameters | Parameter | Default | Max | Description | |---|---|---|---| | `page` | `1` | — | The page number to retrieve (1-indexed). | | `per_page` | `25` | `100` | Number of items per page. | ### Example Request ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles?page=2&per_page=50" \ -H "X-API-Key: sk_xxx" ``` ### Response Format Paginated responses include a `pagination` object alongside `data`: ```json { "data": [ { "id": "bnd_abc123", "status": "published" }, { "id": "bnd_def456", "status": "configuring" } ], "pagination": { "page": 2, "per_page": 50, "total": 137, "total_pages": 3 } } ``` ### Iterating Through All Pages ```typescript async function fetchAllBundles(apiKey: string) { const results = []; let page = 1; while (true) { const res = await fetch( `https://app.tokportal.com/api/ext/bundles?page=${page}&per_page=100`, { headers: { "X-API-Key": apiKey } } ); const { data, pagination } = await res.json(); results.push(...data); if (page >= pagination.total_pages) break; page++; } return results; } ``` ## Idempotency For mutating requests (`POST`, `PUT`, `PATCH`, and `DELETE`), you can include an `Idempotency-Key` header to safely retry requests without creating duplicate resources or double-charging credits. ### Usage ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_xxx" \ -H "Idempotency-Key: my-unique-request-id-12345" \ -H "Content-Type: application/json" \ -d '{ "platform": "tiktok", "country": "US", "videos": 10 }' ``` ### How It Works - The first request with a given `Idempotency-Key` is processed normally and the response is stored. - Subsequent requests with the **same key, method, path, and request body** return the original response without executing the operation again. - Replayed responses include the `Idempotent-Replayed: true` header. - Reusing the same key with a different request body returns `IDEMPOTENCY_KEY_REUSED`. - Retrying while the first request is still running returns `IDEMPOTENCY_KEY_IN_PROGRESS`; retry after a short delay. - Idempotency keys are scoped to your API key and expire after **24 hours**. - Use a unique value per logical operation (e.g., a UUID or a meaningful identifier from your system). ### When to Use Idempotency Keys Idempotency keys are recommended for any operation that: - **Creates a resource** (bundles, video slots, edit slots) - **Debits credits** - **Publishes, deletes, or updates state** - **Could be retried** due to network timeouts or uncertain responses This prevents accidental double-charges or duplicate resource creation. ## Idempotency Errors | Code | HTTP Status | Meaning | |---|---:|---| | `IDEMPOTENCY_KEY_TOO_LONG` | 400 | The key is longer than 255 characters. | | `IDEMPOTENCY_KEY_REUSED` | 409 | The key already exists for a different request body. | | `IDEMPOTENCY_KEY_IN_PROGRESS` | 409 | The first request is still processing. Retry the exact same request shortly. | --- ## Getting Started – First API Call in 5 Minutes Source: api/01-getting-started/05-getting-started.md URL: https://developers.tokportal.com/getting-started # Getting Started Build your first TokPortal integration in 5 minutes. ## Prerequisites - A TokPortal account at [app.tokportal.com](https://app.tokportal.com) - Credits in your account ([buy credits](https://app.tokportal.com/dashboard/credits)) - An API key ([generate one](https://app.tokportal.com/developer/api-keys)) ## Step 1: Generate an API Key Go to the [Developer Portal](https://app.tokportal.com/developer/api-keys) and click **Generate**. Copy your key — it starts with `sk_` and is shown only once. ## Step 2: Verify Your Key ```bash curl https://app.tokportal.com/api/ext/me \ -H "X-API-Key: sk_your_key_here" ``` You should see your profile and credit balance. ## Step 3: Create a Bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "USA", "title": "My First Bundle", "videos_quantity": 5 }' ``` This creates a TikTok account in the USA with 5 video slots. Cost: 35 credits (25 account + 5×2 videos). ## Step 4: Configure the Account ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/{bundle_id}/account \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "username": "mybrand", "visible_name": "My Brand Official", "biography": "Fashion and lifestyle content" }' ``` ## Step 5: Configure Videos Upload a video first, then configure: ```bash # Get a presigned upload URL curl -X POST https://app.tokportal.com/api/ext/upload/video \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"filename": "video1.mp4", "bundle_id": "{bundle_id}"}' # Upload the file (use the upload_url from the response) curl -X PUT "{upload_url}" \ -H "Content-Type: video/mp4" \ --data-binary @video1.mp4 # Configure the video slot curl -X PUT https://app.tokportal.com/api/ext/bundles/{bundle_id}/videos/1 \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "Check out this trend! #fashion", "target_publish_date": "2026-03-01", "video_url": "{public_url}" }' ``` Or use **batch configuration** to set up multiple videos at once, or **CSV import** to import from a spreadsheet. ## Step 6: Publish ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/{bundle_id}/publish \ -H "X-API-Key: sk_your_key_here" ``` Your bundle is now live. An account manager will be assigned and start working on it. ## Step 7: Track Progress ```bash curl https://app.tokportal.com/api/ext/bundles/{bundle_id} \ -H "X-API-Key: sk_your_key_here" ``` Monitor the `status` field on the bundle, account, and each video. ## What's Next? - [Create Bulk bundles](/bundles/create-bulk) for large-scale campaigns - [CSV Import](/videos/csv-import) for spreadsheet-based workflows - [Analytics](/analytics) to track account performance - [Accounts](/accounts/saved-accounts) to manage delivered accounts and retrieve verification codes --- ## OpenAPI, SDKs, CLI & MCP Source: api/01-getting-started/06-sdks-cli.md URL: https://developers.tokportal.com/sdks-cli # OpenAPI, SDKs, CLI & MCP TokPortal exposes public developer tools from the same API schema used for the OpenAPI spec, generated API reference, LLM assets, CLI, SDK, MCP server, and webhook helpers. ## Distribution Matrix | Surface | Distribution | Install / usage | Status | | --------------------- | ------------ | ---------------------------------- | ---------------------- | | OpenAPI | Public docs | `/openapi.json` or `/openapi.yaml` | Public source of truth | | Node / TypeScript SDK | npm | `npm install @tokportal/node` | Public package | | CLI | npm | `npm install -g @tokportal/cli` | Public package | | MCP server | npm | `npx tokportal-mcp` | Public package | Python, Go, Ruby, Java, PHP, .NET, and Rust SDKs are deferred until their registry or repository release path is ready. For those languages today, use the raw HTTP API with `/openapi.json` or `/openapi.yaml`. ## Node / TypeScript SDK Install: ```bash npm install @tokportal/node ``` Create a client: ```ts import { TokPortal } from "@tokportal/node"; const tokportal = new TokPortal({ apiKey: process.env.TOKPORTAL_API_KEY!, }); ``` Create a bundle, configure webhooks, export analytics, and use media upload helpers: ```ts const bundle = await tokportal.bundles.create({ bundle_type: "account_and_videos", country: "USA", videos_quantity: 5, }); const webhook = await tokportal.webhooks.create({ url: "https://example.com/tokportal/webhook", events: ["bundle.created", "bundle.published"], }); const csv = await tokportal.analytics.exportVideos({ account: ["9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"], }); const image = await tokportal.uploads.imageFromUrl({ url: "https://cdn.example.com/photo.jpg", bundle_id: bundle.data.id, purpose: "carousel", }); await tokportal.bundles.fixVideoDownload(bundle.data.id, 1, { video_url: "https://cdn.example.com/replacement.mp4", }); console.log(bundle.data.id, webhook.data.id, csv, image.data.id); ``` Direct multipart uploads can use either a `Blob` or a local file path: ```ts const uploaded = await tokportal.uploads.videoDirectFile( "./video.mp4", bundle.data.id, "video/mp4", { idempotencyKey: "video-upload-123" }, ); console.log(uploaded.data.url); ``` Every OpenAPI operation is also reachable through the generated operation map: ```ts const retry = await tokportal.requestOperation("retryWebhookDelivery", { path: { id: webhook.data.id, delivery_id: "7a1f3e5d-2222-4333-9444-abc123abc123", }, }); ``` ## Webhook Signatures Use the SDK helper with the exact raw request body received by your HTTP framework, before JSON parsing or re-serialization: ```ts import { verifyWebhookSignature } from "@tokportal/node"; const valid = verifyWebhookSignature( rawBody, request.headers["tokportal-signature"], process.env.TOKPORTAL_WEBHOOK_SECRET!, ); ``` See [Webhooks](/webhooks) for the full event catalog, endpoint management API, manual HMAC verification examples, delivery logs, and retry behavior. ## CLI Install: ```bash npm install -g @tokportal/cli ``` Use: ```bash export TOKPORTAL_API_KEY=sk_your_key_here tokportal get-current-user tokportal list-bundles --page 1 --per_page 25 tokportal create-bundle --body '{"bundle_type":"account_and_videos","country":"USA","videos_quantity":5}' tokportal upload-video-direct --file ./video.mp4 --bundle_id 00000000-0000-0000-0000-000000000000 ``` Multipart upload commands are generated from OpenAPI too. Use `--file ` for the binary field and pass form fields such as `--bundle_id` or `--auto_publish` as regular flags. Failed CLI commands print the original API payload plus diagnostics: ```json { "payload": { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded." } }, "diagnostics": { "request_id": "req_...", "retry_after_seconds": 1, "rate_limit": { "limit": 120, "remaining": 0, "reset": 1779724800 } } } ``` ## Structured Errors The Node SDK exposes structured API errors with the HTTP status, TokPortal error code, details, `X-TokPortal-Request-ID`, `Retry-After`, rate limit metadata, and a retryability helper. ```ts import { TokPortalApiError } from "@tokportal/node"; try { await tokportal.bundles.create({ bundle_type: "account_and_videos", country: "USA", videos_quantity: 5, }); } catch (error) { if (error instanceof TokPortalApiError) { console.log(error.status, error.code, error.details, error.requestId); if (error.retryable) { const waitMs = (error.retryAfterSeconds ?? 1) * 1000; // Retry with backoff. } console.log(error.rateLimit?.remaining, error.rateLimit?.reset); } } ``` ## Idempotency Keys Use idempotency keys for mutating requests you may retry after network failures. TokPortal stores the first response for 24 hours and returns the same response for a replay with the same key and request fingerprint. ```ts await tokportal.bundles.create( { bundle_type: "account_and_videos", country: "USA", videos_quantity: 5, }, { idempotencyKey: "bundle-create-123" }, ); ``` ## Client Identification The public Node SDK, CLI, and MCP server send `X-TokPortal-Client` on API requests. This does not affect API behavior; it helps TokPortal support and observability identify the integration surface and version. | Surface | Header | | --------------------- | -------------------------------------------- | | Node / TypeScript SDK | `X-TokPortal-Client: tokportal-node/0.1.0` | | CLI | `X-TokPortal-Client: tokportal-cli/0.1.1` | | MCP server | `X-TokPortal-Client: tokportal-mcp/1.8.0` | ## MCP ```bash npx tokportal-mcp ``` Failed MCP tool calls return an error result containing the original API payload plus `diagnostics.request_id`, `diagnostics.retry_after_seconds`, and `diagnostics.rate_limit` when available. ```json { "mcpServers": { "tokportal": { "command": "tokportal-mcp", "env": { "TOKPORTAL_API_KEY": "sk_your_key_here" } } } } ``` ## Schema Source - OpenAPI JSON: [developers.tokportal.com/openapi.json](https://developers.tokportal.com/openapi.json) - OpenAPI YAML: [developers.tokportal.com/openapi.yaml](https://developers.tokportal.com/openapi.yaml) - LLM summary: [developers.tokportal.com/llms.txt](https://developers.tokportal.com/llms.txt) - Full LLM context: [developers.tokportal.com/llms-full.txt](https://developers.tokportal.com/llms-full.txt) - Developer ecosystem: [developers.tokportal.com/developer-ecosystem.json](https://developers.tokportal.com/developer-ecosystem.json) - Package release manifest: [developers.tokportal.com/package-release-manifest.json](https://developers.tokportal.com/package-release-manifest.json) ## Generated Surface Verification TokPortal keeps the public Node SDK, CLI, MCP tools, OpenAPI, and LLM assets aligned with one verification command: ```bash npm run verify:developer-surface ``` The command rebuilds the public npm packages, regenerates CLI/MCP definitions, checks the public OpenAPI operation count, and rebuilds the docs AI assets when the docs repo is present. To prepare package repository exports locally: ```bash npm run export:developer-packages ``` This writes `dist/developer-packages/tokportal-node`, `tokportal-cli`, and `tokportal-mcp` from the package release manifest. Each export includes `.tokportal-release.json` with the target repo, build command, publish command, schema source, generated `.github/workflows/ci.yml` / `.github/workflows/release.yml`, plus `SECURITY.md`, `CONTRIBUTING.md`, `RELEASE.md`, and `LICENSE`. --- ## AI Context Source: api/01-getting-started/07-ai-context.md URL: https://developers.tokportal.com/ai-context # AI Context TokPortal publishes machine-readable context files so AI tools can understand the API without scraping the site. ## Canonical AI Assets | Asset | URL | Use | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | `llms.txt` | [`developers.tokportal.com/llms.txt`](https://developers.tokportal.com/llms.txt) | Short index for LLM discovery | | `llms-full.txt` | [`developers.tokportal.com/llms-full.txt`](https://developers.tokportal.com/llms-full.txt) | Full docs corpus for ChatGPT, Claude, Cursor, and agent ingestion | | `ai-context.json` | [`developers.tokportal.com/ai-context.json`](https://developers.tokportal.com/ai-context.json) | Structured product, auth, SDK, MCP, docs, and operation metadata | | `developer-ecosystem.json` | [`developers.tokportal.com/developer-ecosystem.json`](https://developers.tokportal.com/developer-ecosystem.json) | Package manager, release status, and SDK distribution metadata | | `package-release-manifest.json` | [`developers.tokportal.com/package-release-manifest.json`](https://developers.tokportal.com/package-release-manifest.json) | Source paths, build commands, publish commands, and release status for generated SDK/CLI/MCP packages | | `openapi.json` | [`developers.tokportal.com/openapi.json`](https://developers.tokportal.com/openapi.json) | OpenAPI 3.1 schema for code generation | | `openapi.yaml` | [`developers.tokportal.com/openapi.yaml`](https://developers.tokportal.com/openapi.yaml) | YAML OpenAPI schema for tools that prefer YAML | ## Recommended Prompts Use this when starting a coding session with an AI assistant: ```text Read https://developers.tokportal.com/llms-full.txt and answer using only the TokPortal API docs. If you need machine-readable package, endpoint, or MCP metadata, read https://developers.tokportal.com/ai-context.json. ``` Use this when generating code: ```text Use the TokPortal OpenAPI schema at https://developers.tokportal.com/openapi.json. Use the public Node SDK, CLI, MCP server, or raw OpenAPI depending on the integration surface. Use X-API-Key authentication with keys formatted as sk_ plus 64 lowercase hex characters. ``` ## What `ai-context.json` Contains - Canonical docs and API URLs. - Authentication rules and API key format. - Mental model: Profiles, Bundles, Accounts, Posts. - SDK, CLI, and MCP package metadata from the TokPortal developer ecosystem. - Release metadata for generated package source and package-manager publishing. - Deferred SDK policy for Ruby, Java, PHP, .NET, and Rust. - Public OpenAPI operations with method, route, operation ID, summary, and tags. - AI docs features: Copy for AI, TokPortal AI chat, and `llms-full.txt`. ## Human Workflow Every docs page includes **Copy for AI**. Use it to copy the current page with source URL and product context into ChatGPT, Claude, or Cursor. The docs also include **Ask TokPortal AI**. It opens a right-side chat drawer, streams answers in real time, and grounds responses in the current page plus the generated `llms-full.txt` corpus. --- ## Versioning & Stability Source: api/01-getting-started/08-versioning.md URL: https://developers.tokportal.com/versioning # Versioning & Stability TokPortal's public API is versioned as a stable contract. The current public API version is: ```http X-TokPortal-API-Version: 2026-05-25 X-TokPortal-API-Stability: stable X-TokPortal-Request-ID: req_... ``` Every public API response includes these headers, including errors, CORS preflight responses, and idempotency replays. Authenticated API responses also include rate limit metadata: ```http X-RateLimit-Limit: 120 X-RateLimit-Remaining: 119 X-RateLimit-Reset: 1779724800 ``` Use `X-TokPortal-Request-ID` to correlate client-side failures with TokPortal logs. You can optionally send `X-Request-ID` on a request; if it is a safe ASCII value up to 128 characters, TokPortal echoes it as `X-TokPortal-Request-ID`. Otherwise TokPortal generates a `req_` identifier. 429 responses include `Retry-After`. ## Compatibility Policy We treat the public API as backwards-compatible by default. Non-breaking changes can ship inside the current version: - New endpoints - New optional request fields - New response fields - New enum values when clients should already tolerate unknown values - New SDK, CLI, MCP, OpenAPI, or documentation surfaces generated from the schema Breaking changes require a new public API version and changelog entry before adoption: - Removing or renaming an endpoint - Removing, renaming, or changing the meaning of a response field - Making an optional request field required - Changing authentication semantics - Changing error codes in a way that breaks existing retry or recovery logic ## Deprecations Deprecated fields or endpoints remain documented during the migration window. The changelog is the source of truth for deprecation timelines and replacement paths. ## Client Identification Generated SDKs, CLI, and MCP clients send: ```http X-TokPortal-Client: tokportal-node/0.1.0 ``` The exact value changes by surface, for example `tokportal-node/0.1.0`, `tokportal-cli/0.1.1`, or `tokportal-mcp/1.8.0`. Custom clients can send their own product/version string for support and diagnostics. --- ## Changelog Source: api/01-getting-started/99-changelog.md URL: https://developers.tokportal.com/changelog # Changelog ## 2026-05-25 - Added first-class OpenAPI assets at `/openapi.json` and `/openapi.yaml`. - Added `llms.txt` and `llms-full.txt` for LLM ingestion. - Added `ai-context.json` and `developer-ecosystem.json` for 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-Client` identification headers across generated SDKs, CLI, and MCP server. - Added public API version and stability response headers: `X-TokPortal-API-Version: 2026-05-25` and `X-TokPortal-API-Stability: stable`. - Added `X-TokPortal-Request-ID` response 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`, and `Retry-After` headers for authenticated public API requests. - Added SDK error metadata for rate limiting and backoff: Node `retryAfterSeconds` and `rateLimit`. - Added Node webhook signature helper docs plus manual HMAC verification examples. - Added CLI and MCP error diagnostics with `request_id`, `retry_after_seconds`, and `rate_limit` metadata. - Added the Versioning & Stability docs page with backwards compatibility and deprecation policy. - Added public webhook endpoint registry at `/webhooks` with emitted bundle, account, video, and test events, delivery logs, and test delivery support. - Added the public webhook event catalog at `/webhooks/events` with 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-Key` handling 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-codeSamples` for every operation across curl, Node, Python, and Go. - Added a generated `/api-reference` docs page from OpenAPI so every operation and code sample is searchable and included in `llms-full.txt`. - Kept legacy API key validation compatible while new keys use the canonical `sk_` format. --- ## Bundles – Campaign Management API Source: api/02-bundles/10-bundles.md URL: https://developers.tokportal.com/bundles # Bundles A **bundle** groups an account configuration and optional video configuration for a specific country and platform. Bundles are the core unit of work in the TokPortal API. ## Authentication All endpoints require an API key passed via the `X-API-Key` header. ``` X-API-Key: sk_xxx ``` ## Base URL ``` https://app.tokportal.com/api/ext ``` ## Bundle Types | Type | Description | |---|---| | `account_only` | Account creation and setup only. No videos. | | `account_and_videos` | Account creation with video uploads. | | `videos_only` | Video uploads on an existing account. | ## Lifecycle Bundles follow a strict lifecycle: ``` pending_setup → published → accepted → completed ``` For bundles that include videos, the video phase adds intermediate states: ``` accepted → in_review → finalized → completed ``` | Status | Description | |---|---| | `pending_setup` | Bundle created, awaiting configuration. | | `published` | Configuration complete, submitted for processing. | | `accepted` | Account work has been accepted by an operator. | | `in_review` | Videos are being reviewed. | | `finalized` | Videos have been finalized. | | `completed` | All work is done. | ## Platforms | Platform | Value | |---|---| | TikTok | `tiktok` | | Instagram | `instagram` | ## Options | Option | Description | Restrictions | |---|---|---| | `wants_niche_warming` | Enable niche warming. | Cannot combine with `wants_deep_warming`; requires `niche_warming_instructions`. | | `wants_deep_warming` | Enable deep warming. | Instagram only. Cannot combine with `wants_niche_warming`. | | `wants_moderation` | Enable content moderation. | Optional `moderation_notice`. | | `edits_quantity` | Add edit slots. | Cannot exceed `videos_quantity`. | | `auto_finalize_videos` | Automatically finalize videos once ready. | — | ## Endpoints | Method | Path | Description | |---|---|---| | `POST` | `/bundles` | [Create a bundle](/bundles/create-bundle) | | `GET` | `/bundles` | List all bundles | | `GET` | `/bundles/:id` | Get a single bundle | | `PATCH` | `/bundles/:id` | Update bundle settings | | `GET` | `/bundles/:id/publish-readiness` | [Check publish-readiness](#check-publish-readiness) | | `POST` | `/bundles/:id/publish` | [Publish a bundle](/bundles/publish-unpublish) | | `POST` | `/bundles/:id/unpublish` | [Unpublish a bundle](/bundles/publish-unpublish) | ## Computed State Fields Both `GET /bundles` and `GET /bundles/:id` return a set of **read-only computed fields** to make integration logic simpler. These derive from the underlying bundle / account / video state — you do not need to recompute them client-side. | Field | Type | Description | |---|---|---| | `is_published` | boolean | `true` once the bundle has been published (status is `published`, `published_priority`, `accepted`, or `completed`). | | `account_configured` | boolean | `true` when the bundle's account is in `configured` or `finalized` status (i.e., publish-eligible). | | `videos_total` | integer | Number of video slots on the bundle. | | `videos_configured_count` | integer | Number of video slots whose status indicates they have been configured (any of `configured`, `in_review`, `pending_corrections`, `accepted`, `published`, `finalized`). | | `next_action` | string \| null | Suggested next step in the configure → publish flow. One of `configure_account`, `configure_videos`, `publish_bundle`, or `null` if no action is required. | `next_action` is the recommended primary signal for an integration: - `configure_account` — call `PUT /bundles/:id/account` to set the account fields. - `configure_videos` — call `PUT /bundles/:id/videos/batch` (or per-position) to fill the video slots. - `publish_bundle` — call `POST /bundles/:id/publish` (or pass `auto_publish: true` on the next video configuration call). - `null` — the bundle is already past setup; nothing to do. --- ## List Bundles Retrieve all bundles for your organization. ``` GET /bundles ``` ### Query Parameters | Param | Type | Description | |---|---|---| | `status` | string | Filter by bundle status: `pending_setup`, `published`, `accepted`, `completed`, etc. | | `bundle_type` | string | Filter by type: `account_only`, `account_and_videos`, `videos_only`. | | `account_status` | string | Filter by account listing status: `pending`, `configured`, `in_review`, `finalized`. Useful for finding bundles with accounts ready to finalize. | | `platform` | string | Filter by platform: `tiktok`, `instagram`, `youtube`. | | `external_ref` | string | Filter by your external reference. | | `page` | integer | Page number (default: 1). | | `per_page` | integer | Results per page (default: 25, max: 100). | ### Example ```bash # List all bundles with accounts awaiting finalization curl -X GET "https://app.tokportal.com/api/ext/bundles?account_status=in_review" \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": [ { "id": "bnd_abc123", "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "status": "accepted", "account_status": "in_review", "videos_quantity": 5, "edits_quantity": 0, "used_edits": 0, "credit_cost": 60, "external_ref": "campaign-42", "existing_account_id": null, "auto_finalize_videos": true, "wants_niche_warming": true, "wants_deep_warming": false, "wants_moderation": false, "is_published": true, "account_configured": true, "videos_total": 5, "videos_configured_count": 5, "next_action": null, "created_at": "2026-01-15T10:30:00Z", "updated_at": "2026-01-15T12:00:00Z" } ] } ``` --- ## Get Bundle Retrieve a single bundle by ID. ``` GET /bundles/:id ``` ### Example ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123 \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": { "id": "bnd_abc123", "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "status": "published", "videos_quantity": 3, "edits_quantity": 0, "used_edits": 0, "credit_cost": 40, "external_ref": "campaign-42", "existing_account_id": null, "auto_finalize_videos": false, "wants_niche_warming": true, "wants_deep_warming": false, "wants_moderation": false, "is_published": true, "account_configured": true, "videos_total": 3, "videos_configured_count": 3, "next_action": null, "account": { "username": "cooluser123", "visible_name": "Cool User", "biography": "Just vibes.", "profile_picture_url": "https://example.com/pic.jpg" }, "videos": [ { "id": "vid_xyz", "position": 1, "status": "pending", "name": "Video 1" } ], "created_at": "2026-01-15T10:30:00Z", "updated_at": "2026-01-15T12:00:00Z" } } ``` --- ## Update Bundle Update settings on an existing bundle. You can modify `auto_finalize_videos`, `external_ref`, and `title` at any time, regardless of bundle status. ``` PATCH /bundles/:id ``` ### Request Body All fields are optional. At least one must be provided. | Field | Type | Description | |---|---|---| | `auto_finalize_videos` | boolean | Toggle auto-approval of videos. | | `external_ref` | string \| null | Your own reference ID. Max 200 characters. Must be unique per user. Set to `null` to clear. | | `title` | string \| null | Bundle title. Max 200 characters. Set to `null` to clear. | ### Example ```bash curl -X PATCH https://app.tokportal.com/api/ext/bundles/bnd_abc123 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "auto_finalize_videos": false, "external_ref": "campaign-42-updated" }' ``` ### Response ```json { "data": { "id": "bnd_abc123", "title": "My Campaign", "auto_finalize_videos": false, "external_ref": "campaign-42-updated", "updated_at": "2026-02-10T14:30:00Z" } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `VALIDATION_ERROR` | No fields provided, or invalid values. | | `404` | `BUNDLE_NOT_FOUND` | Bundle does not exist. | | `403` | `BUNDLE_NOT_OWNED` | Bundle belongs to another user. | | `409` | `DUPLICATE_EXTERNAL_REF` | Another bundle already uses this `external_ref`. | --- ## Check Publish-Readiness Returns whether a bundle is currently ready for `POST /bundles/:id/publish`, and if not, the list of blockers that publish would raise. **Read-only — no side effects, no credit charge.** Useful to surface "what's missing" in your UI before attempting publish. ``` GET /bundles/:id/publish-readiness ``` ### Example ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123/publish-readiness \ -H "X-API-Key: sk_xxx" ``` ### Response — ready ```json { "data": { "bundle_id": "bnd_abc123", "ready": true, "blockers": [] } } ``` ### Response — blockers present ```json { "data": { "bundle_id": "bnd_abc123", "ready": false, "blockers": [ { "code": "ACCOUNT_MISSING_FIELDS", "message": "Account is missing required fields.", "details": { "missing_fields": ["profile_picture_url"] } } ] } } ``` ### Blocker Codes | Code | Meaning | |---|---| | `BUNDLE_ALREADY_PUBLISHED` | The bundle is already published or accepted. | | `BUNDLE_NOT_IN_SETUP` | The bundle's status is not `pending_setup` (e.g., `draft`, `cancelled`). | | `ACCOUNT_MISSING` | No account configuration exists yet for the bundle. | | `ACCOUNT_NOT_CONFIGURED` | The account exists but its status is not yet `configured` or `finalized`. | | `ACCOUNT_MISSING_FIELDS` | The account is missing one or more required fields (`username`, `visible_name`, `profile_picture_url`). | --- ## Create Bundle - Provision Accounts & Videos Source: api/02-bundles/11-create-bundle.md URL: https://developers.tokportal.com/create-bundle # Create Bundle Create a new bundle to provision an account, upload videos, or both. ``` POST /bundles ``` All fields are **top-level** in the request body. There is no `options` wrapper. ## Request Body ### Required | Field | Type | Description | |---|---|---| | `bundle_type` | string | `"account_only"`, `"account_and_videos"`, or `"videos_only"`. | ### Conditional | Field | Type | Required When | Description | |---|---|---|---| | `country` | string | `account_only`, `account_and_videos` | TokPortal country code or supported alias. Use `GET /countries` to list enabled values. Optional for `videos_only` (derived from the saved account). | | `account_id` | string (UUID) | `videos_only` | The saved account ID to upload videos to. **Only valid with `bundle_type: "videos_only"`** — passing it on `account_and_videos` or `account_only` returns `ACCOUNT_ID_NOT_ALLOWED` (this prevents accidentally being charged the account-creation credits while reusing an existing account). | | `videos_quantity` | integer | `account_and_videos`, `videos_only` | Number of video slots. Must be greater than `0`. | ### Optional | Field | Type | Default | Description | |---|---|---|---| | `platform` | string | `"tiktok"` | `"tiktok"` or `"instagram"`. | | `title` | string | — | Bundle title. Max 200 characters. | | `edits_quantity` | integer | `0` | Number of video edits. Cannot exceed `videos_quantity`. | | `wants_niche_warming` | boolean | `false` | Enable niche warming. Mutually exclusive with `wants_deep_warming`. | | `wants_deep_warming` | boolean | `false` | Enable deep warming. **Instagram only.** Mutually exclusive with `wants_niche_warming`. | | `wants_moderation` | boolean | `false` | Enable content moderation. | | `niche_warming_instructions` | string | — | Instructions for niche warming. **Required** when `wants_niche_warming` is `true`. | | `moderation_notice` | string | — | Optional notice for the moderation team. | | `auto_finalize_videos` | boolean | `true` | Automatically finalize videos when they are ready. | | `external_ref` | string | — | Your own reference ID. Max 200 characters. | ## Validation Rules - `wants_niche_warming` and `wants_deep_warming` are **mutually exclusive** — you cannot enable both. - `wants_deep_warming` is only available on `instagram`. - `edits_quantity` cannot exceed `videos_quantity`. - `niche_warming_instructions` is **required** when `wants_niche_warming` is `true`. - `country` must be enabled for your organization. `US` and `GB` aliases are accepted and normalized by the API. ## Response ```json { "data": { "bundle_id": "...", "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "status": "pending_setup", "videos_quantity": 5, "edits_quantity": 2, "external_ref": "my-campaign-001", "created_at": "2026-02-10T09:00:00Z" }, "credits_charged": 41, "credits_remaining": 459, "cost_breakdown": { "account": 25, "videos": 10, "edits": 6, "niche_warming": 0, "deep_warming": 0, "moderation": 0 } } ``` | Field | Type | Description | |---|---|---| | `data.bundle_id` | string | Unique identifier for the created bundle. | | `data.bundle_type` | string | The bundle type as submitted. | | `data.platform` | string | Target platform. | | `data.country` | string | Country code. | | `data.status` | string | Initial bundle status. | | `data.videos_quantity` | integer | Number of video slots (if applicable). | | `data.edits_quantity` | integer | Number of edits (if applicable). | | `data.external_ref` | string | Your external reference (if provided). | | `data.created_at` | string | ISO 8601 timestamp. | | `credits_charged` | integer | Credits deducted for this bundle. | | `credits_remaining` | integer | Your remaining credit balance. | | `cost_breakdown` | object | Itemized cost breakdown. | --- ## Examples ### 1. Account and Videos (with niche warming) ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 5, "edits_quantity": 2, "wants_niche_warming": true, "niche_warming_instructions": "Focus on fitness and gym content. Follow fitness influencers, like workout videos.", "auto_finalize_videos": true, "external_ref": "campaign-42" }' ``` **Response:** ```json { "data": { "bundle_id": "bnd_a1b2c3d4", "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "status": "pending_setup", "videos_quantity": 5, "edits_quantity": 2, "external_ref": "campaign-42", "created_at": "2026-02-10T09:00:00Z" }, "credits_charged": 48, "credits_remaining": 452, "cost_breakdown": { "account": 25, "videos": 10, "edits": 6, "niche_warming": 7, "deep_warming": 0, "moderation": 0 } } ``` ### 2. Account Only ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_only", "platform": "instagram", "country": "FR", "wants_deep_warming": true, "title": "French lifestyle account" }' ``` **Response:** ```json { "data": { "bundle_id": "bnd_e5f6g7h8", "bundle_type": "account_only", "platform": "instagram", "country": "FR", "status": "pending_setup", "videos_quantity": 0, "edits_quantity": 0, "external_ref": null, "created_at": "2026-02-10T09:05:00Z" }, "credits_charged": 65, "credits_remaining": 387, "cost_breakdown": { "account": 25, "videos": 0, "edits": 0, "niche_warming": 0, "deep_warming": 40, "moderation": 0 } } ``` ### 3. Videos Only (existing account) > **Recommendation:** Instead of creating a `videos_only` bundle, consider using the [add-video-slots](publish-unpublish) endpoint on an existing active bundle. This preserves the same account manager, which is better for continuity and turnaround time. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "videos_only", "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "videos_quantity": 3, "wants_moderation": true, "moderation_notice": "Ensure no competitor logos are visible", "external_ref": "restock-wave-2" }' ``` **Response:** ```json { "data": { "bundle_id": "bnd_i9j0k1l2", "bundle_type": "videos_only", "platform": "tiktok", "country": "US", "status": "pending_setup", "videos_quantity": 3, "edits_quantity": 0, "external_ref": "restock-wave-2", "created_at": "2026-02-10T09:10:00Z" }, "credits_charged": 31, "credits_remaining": 356, "cost_breakdown": { "account": 0, "videos": 6, "edits": 0, "niche_warming": 0, "deep_warming": 0, "moderation": 25 } } ``` > Note: `platform` and `country` are derived from the saved account when using `videos_only`. ## Error Responses | Status | Code | Description | |---|---|---| | `400` | `VALIDATION_ERROR` | Invalid or missing fields (see `details` for specifics). | | `400` | `WARMING_CONFLICT` | `wants_niche_warming` and `wants_deep_warming` cannot both be enabled. | | `400` | `DEEP_WARMING_INSTAGRAM_ONLY` | `wants_deep_warming` is only available on Instagram. | | `400` | `EDITS_EXCEED_VIDEOS` | `edits_quantity` exceeds `videos_quantity`. | | `400` | `COUNTRY_NOT_ENABLED` | Country is not enabled for your organization. | | `400` | `ACCOUNT_ID_NOT_ALLOWED` | `account_id` was passed with a `bundle_type` other than `videos_only`. Set `bundle_type` to `videos_only` to add videos to an existing account, or omit `account_id`. | | `402` | `INSUFFICIENT_CREDITS` | Not enough credits to create this bundle. | | `404` | `ACCOUNT_NOT_FOUND` | `account_id` does not exist or does not belong to your organization. | --- ## Create Bulk - Multiple Bundles Source: api/02-bundles/12-create-bulk.md URL: https://developers.tokportal.com/create-bulk # Create Bulk Create multiple bundles in one request. ```http POST /bundles/bulk ``` The bulk endpoint creates `accounts_count x platforms.length` bundles for one country. It debits credits atomically for the whole batch. ## Request Body All fields are top-level. There is no `options` wrapper and no `metadata` field in the public contract. | Field | Type | Required | Default | Description | |---|---|---:|---|---| | `platforms` | string[] | Yes | - | One or more platforms. Public values: `tiktok`, `instagram`. | | `country` | string | Yes | - | TokPortal country code or supported alias. Use `GET /countries` to list enabled values. | | `accounts_count` | integer | Yes | - | Number of accounts to create per platform. Min `1`, max `100`. | | `upload_accounts_count` | integer | No | `0` | How many accounts per platform should receive video slots. Must be `<= accounts_count`. The rest are `account_only` bundles. | | `videos_per_account` | integer | No | `0` | Number of video slots for each account that receives videos. Max `500`. | | `wants_niche_warming` | boolean | No | `false` | Enable niche warming. Mutually exclusive with `wants_deep_warming`. | | `wants_deep_warming` | boolean | No | `false` | Enable deep warming. Instagram only. Mutually exclusive with `wants_niche_warming`. | | `wants_moderation` | boolean | No | `false` | Enable content moderation. | | `niche_warming_instructions` | string | No | - | Instructions for niche warming. Required when `wants_niche_warming` is `true`. | | `moderation_notice` | string | No | - | Optional notice for moderation. | | `auto_finalize_videos` | boolean | No | `true` | Automatically finalize videos when ready. | | `external_ref` | string | No | - | Your external reference. Max 200 characters. | ## How It Works Example: ```json { "accounts_count": 10, "upload_accounts_count": 3, "platforms": ["tiktok", "instagram"], "country": "US", "videos_per_account": 5 } ``` Result: | Platform | `account_and_videos` | `account_only` | Total bundles | |---|---:|---:|---:| | TikTok | 3 | 7 | 10 | | Instagram | 3 | 7 | 10 | | **Total** | **6** | **14** | **20** | Total video slots: `3 upload accounts x 2 platforms x 5 videos = 30`. ## Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "accounts_count": 5, "upload_accounts_count": 2, "platforms": ["tiktok", "instagram"], "country": "US", "videos_per_account": 3, "wants_niche_warming": true, "niche_warming_instructions": "Lifestyle and product review content.", "wants_moderation": true, "external_ref": "q2-launch-us" }' ``` ## Response ```json { "data": { "bundles_created": 10, "bundles": [ { "id": "bnd_001", "platform": "tiktok", "bundle_type": "account_and_videos" }, { "id": "bnd_002", "platform": "tiktok", "bundle_type": "account_only" } ], "summary": { "platforms": ["tiktok", "instagram"], "accounts_per_platform": 5, "accounts_with_videos": 2, "accounts_without_videos": 3, "videos_per_account": 3 } }, "credits_charged": 594, "credits_remaining": 9406, "cost_breakdown": { "per_account_with_videos": { "account_creation": 25, "video_slots": 6, "edit_slots": 0, "niche_warming": 7, "deep_warming": 0, "moderation": 25, "total": 63 }, "per_account_only": { "account_creation": 25, "video_slots": 0, "edit_slots": 0, "niche_warming": 7, "deep_warming": 0, "moderation": 25, "total": 57 }, "accounts_with_videos": 2, "accounts_without_videos": 3, "platforms_count": 2, "total": 594 } } ``` The sample is shortened. The real response includes every created bundle and the server-calculated credit breakdown. ## Validation Rules - `upload_accounts_count` cannot exceed `accounts_count`. - If `videos_per_account` is greater than `0`, set `upload_accounts_count` greater than `0` to create upload-capable bundles. - `wants_niche_warming` and `wants_deep_warming` are mutually exclusive. - `wants_deep_warming` is only valid for Instagram. If `platforms` includes TikTok, do not set `wants_deep_warming: true`. - `niche_warming_instructions` is required when `wants_niche_warming` is `true`. - `country` must be enabled for your organization. ## Countries The endpoint accepts one country per request. To create bundles in several countries, loop over country codes and call `POST /bundles/bulk` once per country. Use: ```bash curl -X GET https://app.tokportal.com/api/ext/countries \ -H "X-API-Key: sk_xxx" ``` ## Error Responses | Status | Code | Description | |---|---|---| | `400` | `INVALID_BODY` | Invalid request body or unsupported field. | | `400` | `INVALID_COUNTRY` | Country is not enabled. | | `400` | `WARMING_CONFLICT` | Niche warming and deep warming cannot both be enabled. | | `400` | `DEEP_WARMING_INSTAGRAM_ONLY` | Deep warming is only valid for Instagram-only requests. | | `402` | `INSUFFICIENT_CREDITS` | Not enough credits for the full bulk operation. | | `429` | `RATE_LIMIT_EXCEEDED` | Create rate limit exceeded. | ## Next Steps - [Configure account profiles](/account-configuration) for created bundles. - [Configure videos](/configure-videos) for `account_and_videos` bundles. - [Publish bundles](/publish-unpublish) when they are ready. --- ## Publish & Unpublish Bundles via API Source: api/02-bundles/13-publish-unpublish.md URL: https://developers.tokportal.com/publish-unpublish # Publish & Unpublish Control the lifecycle of a bundle by publishing, unpublishing, or adding slots after creation. --- ## Publish Submit a configured bundle for processing. The account must be fully configured, and for `account_and_videos` bundles, at least one video must be configured. ``` POST /bundles/:id/publish ``` ### Requirements - Bundle must be in `pending_setup` status. - Account configuration must be complete (username, visible name, biography, profile picture). - For `account_and_videos` type: at least 1 video must be configured. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/publish \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": { "id": "bnd_abc123", "status": "published", "published_at": "2026-02-10T14:00:00Z" } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `account_not_configured` | Account configuration is incomplete. | | `400` | `no_videos_configured` | At least 1 video is required for `account_and_videos` bundles. | | `409` | `invalid_status` | Bundle is not in `pending_setup` status. | --- ## Unpublish Revert a published bundle back to `pending_setup`. This allows you to make further configuration changes before resubmitting. ``` POST /bundles/:id/unpublish ``` :::warning Unpublishing does **not** refund credits. The credits charged at bundle creation remain deducted. ::: ### Requirements - Bundle must be in `published` status. - Bundle must not have been accepted yet. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/unpublish \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": { "id": "bnd_abc123", "status": "pending_setup", "unpublished_at": "2026-02-10T15:30:00Z" } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `409` | `invalid_status` | Bundle is not in `published` status. | | `409` | `already_accepted` | Bundle has been accepted and cannot be unpublished. | --- ## Add Video Slots Add additional video slots to an existing bundle. Credits are debited immediately. ``` POST /bundles/:id/add-video-slots ``` ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `quantity` | integer | Yes | Number of video slots to add. Must be `>= 1`. | ### Requirements - Bundle type must be `account_and_videos` or `videos_only`. - Bundle must not be in `completed` status. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/add-video-slots \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "quantity": 2 }' ``` ### Response ```json { "data": { "id": "bnd_abc123", "videos_quantity": 5, "previous_videos_quantity": 3 }, "credits_charged": 20, "credits_remaining": 350 } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `invalid_quantity` | Quantity must be at least 1. | | `402` | `insufficient_credits` | Not enough credits. | | `409` | `BUNDLE_TYPE_INCOMPATIBLE` | Bundle is `account_only`. Create a `videos_only` bundle instead. | | `409` | `bundle_completed` | Cannot add slots to a completed bundle. | --- ## Add Edit Slots Add editing slots to an existing bundle. Each video can have at most 1 edit slot. Credits are debited immediately. ``` POST /bundles/:id/add-edit-slots ``` ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `quantity` | integer | Yes | Number of edit slots to add. Must be `>= 1`. | ### Requirements - Bundle type must be `account_and_videos` or `videos_only`. - Total `edits_quantity` must not exceed `videos_quantity` (max 1 edit per video). - Bundle must not be in `completed` status. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/add-edit-slots \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "quantity": 1 }' ``` ### Response ```json { "data": { "id": "bnd_abc123", "edits_quantity": 3, "previous_edits_quantity": 2, "videos_quantity": 5 }, "credits_charged": 5, "credits_remaining": 345 } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `invalid_bundle_type` | Bundle type does not support videos/edits. | | `400` | `edits_exceed_videos` | Adding these slots would exceed the 1-edit-per-video limit. | | `400` | `invalid_quantity` | Quantity must be at least 1. | | `402` | `insufficient_credits` | Not enough credits. | | `409` | `bundle_completed` | Cannot add slots to a completed bundle. | --- ## Account Configuration – Profile Setup API Source: api/03-account-config/20-accounts.md URL: https://developers.tokportal.com/account-configuration # Account Configuration Configure the account details for a bundle. Account configuration is required before a bundle can be published. --- ## Configure Account Set or update the account details for a bundle. ``` PUT /bundles/:id/account ``` ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `username` | string | Yes | Account username. 1-24 characters, alphanumeric plus `.` and `_`. | | `visible_name` | string | Yes | Display name. 1-30 characters. | | `biography` | string | Yes | Account biography. Max 80 characters. | | `profile_picture_url` | string | Yes | URL to the profile picture image. | | `link_in_bio` | string | No | Link displayed in the bio. **Instagram only.** | | `niche_warming_instructions` | string | No | Instructions for niche warming behavior. Only applies when `niche_warming` is enabled. | ### Validation Rules | Field | Rule | |---|---| | `username` | 1-24 characters. Allowed: `[a-zA-Z0-9_.]` | | `visible_name` | 1-30 characters. | | `biography` | Max 80 characters. | | `profile_picture_url` | Must be a valid URL. | | `link_in_bio` | Must be a valid URL. Instagram only. | :::warning Avoid duplicated wording across accounts Do not create multiple accounts that reuse the same words in the `username` or `visible_name`. Prefer unique usernames and nicknames for every account. Reusing the same words across many accounts on the same platform increases the risk of mass-ban detection by the platform's anti-spam systems. ::: ### Requirements - Bundle must be in `pending_setup` or `configured` status. - You cannot modify account configuration after the bundle has been published (unpublish first). ### Example ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "username": "streetstyle.nyc", "visible_name": "Street Style NYC", "biography": "Daily street fashion from New York City.", "profile_picture_url": "https://cdn.example.com/profiles/streetstyle.jpg", "niche_warming_instructions": "Engage with fashion and streetwear content in NYC area." }' ``` ### Response ```json { "data": { "id": "acc_xyz789", "bundle_id": "bnd_abc123", "status": "configured", "platform": "tiktok", "username": "streetstyle.nyc", "visible_name": "Street Style NYC", "biography": "Daily street fashion from New York City.", "profile_picture_url": "profile-pictures/bnd_abc123/streetstyle.jpg", "link_in_bio": null, "country": "US", "is_niche_warmed": false, "is_deep_warmed": false, "deep_warming_verification_status": null, "niche_warming_instructions": "Engage with fashion and streetwear content in NYC area.", "feedback": null, "created_at": "2026-02-10T10:00:00Z" } } ``` :::note Response shape The account fields are returned **flat under `data`** — there is no nested `data.account` wrapper. The `id` field is the **account ID** (not the bundle ID). Use `bundle_id` to reference the parent bundle. If an external `profile_picture_url` was provided, it is automatically downloaded and re-hosted; the response will include `_info.profile_picture_rehosted: true`. If the profile picture is missing, the response will include a `_warnings` array. ::: ### Instagram Example with Link in Bio ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_d4e5f6/account \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "username": "cafe.paris_", "visible_name": "Caf\u00e9 Paris", "biography": "Les meilleurs caf\u00e9s parisiens.", "profile_picture_url": "https://cdn.example.com/profiles/cafeparis.jpg", "link_in_bio": "https://cafeparis.example.com" }' ``` ### Response ```json { "data": { "id": "acc_pqr456", "bundle_id": "bnd_d4e5f6", "status": "configured", "platform": "instagram", "username": "cafe.paris_", "visible_name": "Caf\u00e9 Paris", "biography": "Les meilleurs caf\u00e9s parisiens.", "profile_picture_url": "profile-pictures/bnd_d4e5f6/cafeparis.jpg", "link_in_bio": "https://cafeparis.example.com", "country": "FR", "is_niche_warmed": false, "is_deep_warmed": false, "deep_warming_verification_status": null, "niche_warming_instructions": null, "feedback": null, "created_at": "2026-02-10T10:15:00Z" } } ``` --- ## Get Account Retrieve the current account configuration for a bundle. ``` GET /bundles/:id/account ``` ### Example ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": { "id": "acc_xyz789", "bundle_id": "bnd_abc123", "status": "configured", "platform": "tiktok", "username": "streetstyle.nyc", "visible_name": "Street Style NYC", "biography": "Daily street fashion from New York City.", "profile_picture_url": "profile-pictures/bnd_abc123/streetstyle.jpg", "link_in_bio": null, "country": "US", "is_niche_warmed": false, "is_deep_warmed": false, "deep_warming_verification_status": null, "niche_warming_instructions": "Engage with fashion and streetwear content in NYC area.", "feedback": null, "created_at": "2026-02-10T10:00:00Z" } } ``` ## Error Responses | Status | Code | Description | |---|---|---| | `400` | `invalid_username` | Username does not match allowed pattern `[a-zA-Z0-9_.]` or exceeds 24 characters. | | `400` | `invalid_visible_name` | Visible name is empty or exceeds 30 characters. | | `400` | `biography_too_long` | Biography exceeds 80 characters. | | `400` | `invalid_url` | `profile_picture_url` or `link_in_bio` is not a valid URL. | | `400` | `link_in_bio_instagram_only` | `link_in_bio` is only supported on Instagram. | | `409` | `invalid_status` | Bundle is not in `pending_setup` or `configured` status. | | `409` | `DUPLICATE_ACCOUNT_BUNDLE` | An active bundle already exists for this username on the same platform. `details.existing_bundle_id` contains the conflicting bundle. Does not apply to `videos_only` bundles. | | `404` | `bundle_not_found` | Bundle does not exist. | --- ## Account Review & Approval Actions Source: api/03-account-config/21-account-actions.md URL: https://developers.tokportal.com/account-actions # Account Actions Review and provide feedback on accounts that are in the `in_review` status. You can either finalize the account to approve it, or request corrections with specific feedback. --- ## Finalize Account Approve an account that is currently in review. This marks the account as validated and advances the bundle lifecycle. ``` POST /bundles/:id/account/finalize ``` ### Requirements - Bundle must have an account in `in_review` status. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/account/finalize \ -H "X-API-Key: sk_xxx" ``` ### Response ```json { "data": { "id": "bnd_abc123", "account": { "status": "finalized", "finalized_at": "2026-02-10T16:00:00Z" } } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `409` | `invalid_status` | Account is not in `in_review` status. | | `404` | `bundle_not_found` | Bundle does not exist. | --- ## Request Corrections Request changes to an account that is in review. Provide a general comment and specify which fields need corrections. ``` POST /bundles/:id/account/corrections ``` ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `comment` | string | Yes | General feedback describing what needs to change. | | `fields` | object | Yes | Object specifying which fields need corrections and the feedback for each. | ### Fields Object Each key corresponds to an account field. The value is a string describing the required correction. | Field | Type | Description | |---|---|---| | `username` | string | Feedback on the username. | | `visible_name` | string | Feedback on the display name. | | `biography` | string | Feedback on the biography. | | `profile_picture` | string | Feedback on the profile picture. | You only need to include the fields that require corrections. ### Requirements - Bundle must have an account in `in_review` status. - At least one field must be specified in the `fields` object. ### Example ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/account/corrections \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "comment": "Username needs to be shorter and biography should mention the city.", "fields": { "username": "Please shorten to under 15 characters and remove special characters.", "biography": "Add a mention of New York City to align with the niche." } }' ``` ### Response ```json { "data": { "id": "bnd_abc123", "account": { "status": "corrections_requested", "corrections": { "comment": "Username needs to be shorter and biography should mention the city.", "fields": { "username": "Please shorten to under 15 characters and remove special characters.", "biography": "Add a mention of New York City to align with the niche." }, "requested_at": "2026-02-10T16:30:00Z" } } } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `missing_comment` | The `comment` field is required. | | `400` | `missing_fields` | At least one field must be specified. | | `400` | `invalid_field` | An unrecognized field was included. | | `409` | `invalid_status` | Account is not in `in_review` status. | | `404` | `bundle_not_found` | Bundle does not exist. | --- ## Feedback History Each correction request is recorded in the bundle's feedback history. You can retrieve the full history by fetching the bundle details. ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123 \ -H "X-API-Key: sk_xxx" ``` The response includes a `feedback_history` array on the account object: ```json { "data": { "id": "bnd_abc123", "account": { "status": "in_review", "feedback_history": [ { "type": "corrections_requested", "comment": "Username needs to be shorter and biography should mention the city.", "fields": { "username": "Please shorten to under 15 characters and remove special characters.", "biography": "Add a mention of New York City to align with the niche." }, "requested_at": "2026-02-10T16:30:00Z", "requested_by": "api" }, { "type": "corrections_applied", "applied_at": "2026-02-10T17:00:00Z", "changes": { "username": { "from": "streetstyle.newyorkcity", "to": "streetstyle.nyc" }, "biography": { "from": "Daily street fashion inspiration.", "to": "Daily street fashion from New York City." } } } ] } } } ``` ### Feedback Types | Type | Description | |---|---| | `corrections_requested` | A correction was requested via the API. | | `corrections_applied` | The operator applied the requested corrections. | | `finalized` | The account was approved and finalized. | Each entry in the history is immutable and includes a timestamp for auditability. --- ## Video Management – Types, Formats & Statuses Source: api/04-videos/30-videos.md URL: https://developers.tokportal.com/videos # Videos Videos are the content units within a [bundle](bundles). Each video occupies a specific **position** (1-indexed) in the bundle and represents a single piece of content to be published on a creator's social media account. ## Video Types Every video has a `video_type` that determines what content it contains: | Type | Description | |---|---| | `video` | A single video file | | `carousel` | Multiple images displayed as a slideshow | ## Platform-Specific Behavior Each platform supports different combinations of video types and has its own requirements. ### TikTok | Type | Requirements | |---|---| | `video` | Video file required. Sound URL optional. | | `carousel` | Carousel images required. Sound URL **required**. | ### Instagram Instagram requires the `instagram_content_type` field to distinguish between reels and feed posts: | Content Type | Video Type | Result | |---|---|---| | `reel` | `video` | Reel Video | | `reel` | `carousel` | **Reel Fixed Photos** — creates a **video** from images (not swipeable) | | `post` | `video` | Post Video | | `post` | `carousel` | **Post Carousel** — swipeable photo carousel | :::info Reel Fixed Photos vs Post Carousel On Instagram, `video_type: "carousel"` behaves differently depending on `instagram_content_type`: - With `"reel"`: Instagram creates a **video** from your images (Fixed Photos — not swipeable). This is a Reel, not a carousel. - With `"post"`: Instagram creates a **swipeable photo carousel** in the feed. ::: ### YouTube YouTube support is not available in the current public API. You may see reserved YouTube fields in templates or generated schemas, but public bundle creation currently supports TikTok and Instagram. ## Video Status Lifecycle Each video progresses through the following statuses: ``` pending → configured → published → accepted → in_review → finalized ``` | Status | Description | |---|---| | `pending` | Position exists but no content has been configured yet. | | `configured` | Content has been uploaded and metadata set. Ready for review. | | `published` | The video has been published to the platform by the creator. | | `accepted` | The brand has reviewed and accepted the published video. | | `in_review` | The video is undergoing final review before completion. | | `finalized` | The video lifecycle is complete. No further changes allowed. | ## Publish Date Rules The `target_publish_date` field controls when the video should be published. - **New accounts** (not yet delivered): minimum **3 days** from today. - **Existing accounts** (already delivered): minimum **1 day** from today. Dates earlier than the allowed minimum are rejected with a `VALIDATION_ERROR`. ## List All Videos in a Bundle Retrieve every video configured for a bundle. ``` GET /bundles/:id/videos ``` ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": [ { "position": 1, "video_type": "video", "status": "configured", "description": "Unboxing the new product line", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/abc123.mp4", "external_ref": "campaign-42-v1", "download_issue": false, "download_issue_comment": null }, { "position": 2, "video_type": "carousel", "status": "pending", "description": null, "target_publish_date": null, "video_url": null, "external_ref": null, "download_issue": false, "download_issue_comment": null } ] } ``` ## Get a Single Video ``` GET /bundles/:id/videos/:position ``` ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "position": 1, "video_type": "video", "status": "configured", "description": "Unboxing the new product line", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/abc123.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/original-sound-123", "editing_instructions": "Add brand logo at the end", "external_ref": "campaign-42-v1", "download_issue": false, "download_issue_comment": null } } ``` ## What's Next | Topic | Description | |---|---| | [Configure Videos](/videos/configure-videos) | Set content, metadata, and platform-specific fields | | [Patch Video Metadata](/videos/configure-videos#patch-video-metadata) | Update `external_ref` or `name` at any time without re-uploading content | | [CSV Import](/videos/csv-import) | Bulk-import videos from a CSV file | | [Video Actions](/videos/video-actions) | Finalize, request corrections, or unschedule videos | | [Fix Download Link](/videos/fix-download-link) | Detect and fix broken download links flagged by managers | --- ## Configure Videos – Metadata & Scheduling API Source: api/04-videos/31-configure-videos.md URL: https://developers.tokportal.com/configure-videos # Configure Videos Set content and metadata for videos within a bundle. You can configure a single video by position or update multiple videos in one batch request. ## Configure a Single Video ``` PUT /bundles/:id/videos/:position ``` Sets or updates the content and metadata for the video at the given position (1-indexed). ## Patch Video Metadata ``` PATCH /bundles/:id/videos/:position ``` Update lightweight metadata fields (`external_ref`, `name`) on a video **without** re-uploading content or changing its status. Works at any point in the video's lifecycle (including `published`, `accepted`, `finalized`). Use this endpoint to fix or set your own reference IDs after the fact — no need to re-send the full configuration. ### Request Body All fields are optional. At least one must be provided. | Field | Type | Description | |---|---|---| | `external_ref` | string \| null | Your own reference ID for this video. Max 200 characters. Set to `null` to clear. | | `name` | string \| null | Internal name for the video. Max 200 characters. Set to `null` to clear. | ### Example ```bash curl -X PATCH https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "external_ref": "correct-campaign-id-42" }' ``` ### Response Returns the full video object (same shape as `GET /bundles/:id/videos/:position`). ```json { "data": { "id": "vid_xyz", "position": 1, "status": "published", "external_ref": "correct-campaign-id-42", "name": "Video 1", "...": "..." } } ``` ### Error Responses | Status | Code | Description | |---|---|---| | `400` | `VALIDATION_ERROR` | No fields provided, or invalid values. | | `404` | `BUNDLE_NOT_FOUND` | Bundle does not exist. | | `404` | `VIDEO_NOT_FOUND` | No video exists at this position. | | `403` | `BUNDLE_NOT_OWNED` | Bundle belongs to another user. | --- ## Configure Multiple Videos (Batch) ``` PUT /bundles/:id/videos/batch ``` Updates multiple videos in a single request. The request body must be wrapped in a `videos` object — it is **not** a raw array. ```json { "videos": [ { "position": 1, "video_type": "video", "..." : "..." }, { "position": 2, "video_type": "carousel", "..." : "..." } ] } ``` --- ## `auto_publish` — configure and publish in one call Both `PUT /bundles/:id/videos/:position` and `PUT /bundles/:id/videos/batch` accept an optional top-level boolean `auto_publish` (default `false`). When `true`, the API will attempt `POST /bundles/:id/publish` immediately after a successful video configuration. A common integration mistake is to configure videos and then forget to publish — `auto_publish: true` removes that step. For `POST /bundles/:id/videos/import-csv` (multipart form), pass `auto_publish=true` as a separate form field alongside `file`. ### Behaviour - The configuration step is the source of truth: if it succeeds, the response is a success response. `auto_publish` failures **never** fail the request. - If publish is not currently possible (account not yet configured, missing fields…), the response includes a `blockers` array — same codes as [`GET /bundles/:id/publish-readiness`](/bundles#check-publish-readiness). - If publish was attempted and succeeded, the bundle moves to `published` (or `published_priority`) and a notification is created, identical to a manual `POST /bundles/:id/publish`. ### `auto_publish` response object Every video-configuration response now includes an `auto_publish` field: ```json { "auto_publish": { "attempted": true, "published": true, "bundle_status": "published", "videos_published": 5 } } ``` | Field | Type | Description | |---|---|---| | `attempted` | boolean | `true` only if `auto_publish: true` was sent **and** at least one video was successfully configured in this call. | | `published` | boolean | `true` if publish succeeded. | | `bundle_status` | string | New bundle status (only present when `published: true`). | | `videos_published` | integer | Number of configured videos included in the publish (only when `published: true`). | | `blockers` | array | Present when `published: false` because publish-readiness checks failed. Each item: `{ code, message, details? }`. See [Publish-Readiness](/bundles#check-publish-readiness) for the code list. | | `error` | object | Present when publish was attempted but failed for a non-readiness reason (e.g., transient RPC failure). Shape: `{ code, message }`. Re-attempt by calling `POST /bundles/:id/publish` directly. | ### Example — configure batch and publish ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "auto_publish": true, "videos": [ { "position": 1, "video_type": "video", "video_url": "https://...", "description": "...", "target_publish_date": "2026-05-01" } ] }' ``` ### Example — `auto_publish` skipped because the account is not ready ```json { "data": { "bundle_id": "bnd_abc123", "configured": 5, "results": [...], "auto_publish": { "attempted": true, "published": false, "blockers": [ { "code": "ACCOUNT_MISSING_FIELDS", "message": "Account is missing required fields.", "details": { "missing_fields": ["profile_picture_url"] } } ] } } } ``` ## Field Reference ### Common Fields | Field | Type | Required | Description | |---|---|---|---| | `position` | integer | Batch only | 1-indexed video position. Required in batch requests. | | `video_type` | string | Yes | `"video"` or `"carousel"`. For Instagram: `carousel` with `instagram_content_type: "reel"` = Fixed Photos (creates a video from images), `carousel` with `instagram_content_type: "post"` = swipeable Carousel. | | `description` | string | Yes | Caption or description for the post. | | `target_publish_date` | string | Yes | Desired publish date (`YYYY-MM-DD`). Min 3 days ahead for new accounts, 1 day for existing. | | `video_url` | string | Conditional | Video file URL. Required when `video_type` is `"video"`. You can pass a `public_url` from the upload endpoint **or any external URL** (Google Drive, Dropbox, direct link…) — the API will automatically download and re-upload the file to our storage. | | `carousel_images` | string[] | Conditional | Array of image storage paths. Required when `video_type` is `"carousel"`. Use the `storage_path` from the image upload response. | | `tiktok_sound_url` | string | Conditional | TikTok sound URL. Required for TikTok carousels, optional for TikTok videos. | | `volume_original_sound` | integer | No | Volume of the original video sound, from `0` to `200` (percent). `100` = unchanged. Setting this for the first time on a video costs **1 credit** (see note below). | | `volume_added_sound` | integer | No | Volume of the added sound/music track, from `0` to `200` (percent). `100` = unchanged. Setting this for the first time on a video costs **1 credit** (see note below). | | `editing_instructions` | string | No | Free-text instructions for the editor (e.g., "Add logo at end"). | | `external_ref` | string | No | Your own reference ID for this video (e.g., campaign or CMS identifier). | | `profile_picture_url` | string | No | Storage path for the account profile picture. Use the `storage_path` from the image upload response. | :::info Sound Volume Control — 1 credit per video Setting `volume_original_sound` or `volume_added_sound` on a video enables the sound-volume-control feature for that video and costs **1 credit**, debited automatically the first time either field is set. - Both fields accept integers from `0` (mute) to `200` (double volume). `100` means the sound is untouched. - The credit is charged **once per video**. Subsequent updates to the same fields on the same video are free. - If your balance is too low, the API returns `INSUFFICIENT_CREDITS` with `feature: "sound_volume"`. - Supported on TikTok and Instagram. ::: ### Instagram-Specific Fields | Field | Type | Required | Description | |---|---|---|---| | `instagram_content_type` | string | Yes (Instagram) | `"reel"` or `"post"`. Required for Instagram bundles. | | `instagram_location` | string | No | Location tag for the Instagram post. | | `instagram_collaborators` | string[] | No | Array of Instagram usernames to tag as collaborators. | | `instagram_audio_name` | string | No | Name of the audio track for Instagram Reels. | | `instagram_add_to_story` | boolean | No | If `true`, the creator also shares the post to their Instagram Story. | ### Instagram Content Type Combinations | `instagram_content_type` | `video_type` | Result | |---|---|---| | `reel` | `video` | Reel Video | | `reel` | `carousel` | Reel Fixed Photos — creates a **video** from images (not swipeable) | | `post` | `video` | Post Video | | `post` | `carousel` | Post Carousel — swipeable photo carousel | ### YouTube Fields (Coming Soon) YouTube support is not yet available. The following fields are reserved for future use: | Field | Type | Description | |---|---|---| | `youtube_title` | string | Video title. | | `youtube_tags` | string[] | Array of tags. | | `youtube_category` | string | Video category. | | `youtube_visibility` | string | `"public"`, `"unlisted"`, or `"private"`. | --- ## Examples ### TikTok Video ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "Check out this new product! #ad #sponsored", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/promo.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/trending-sound-789", "editing_instructions": "Add brand watermark in bottom-right corner", "external_ref": "campaign-42-v1" }' ``` **Response:** ```json { "data": { "position": 1, "video_type": "video", "status": "configured", "description": "Check out this new product! #ad #sponsored", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/promo.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/trending-sound-789", "editing_instructions": "Add brand watermark in bottom-right corner", "external_ref": "campaign-42-v1" } } ``` ### TikTok Carousel ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/2 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "carousel", "description": "Swipe through our latest collection", "target_publish_date": "2026-03-17", "carousel_images": [ "carousel-images/org_xxx/bundle_abc123/slide1.jpg", "carousel-images/org_xxx/bundle_abc123/slide2.jpg", "carousel-images/org_xxx/bundle_abc123/slide3.jpg" ], "tiktok_sound_url": "https://www.tiktok.com/music/chill-vibes-456", "external_ref": "campaign-42-v2" }' ``` **Response:** ```json { "data": { "position": 2, "video_type": "carousel", "status": "configured", "description": "Swipe through our latest collection", "target_publish_date": "2026-03-17", "carousel_images": [ "carousel-images/org_xxx/bundle_abc123/slide1.jpg", "carousel-images/org_xxx/bundle_abc123/slide2.jpg", "carousel-images/org_xxx/bundle_abc123/slide3.jpg" ], "tiktok_sound_url": "https://www.tiktok.com/music/chill-vibes-456", "external_ref": "campaign-42-v2" } } ``` ### Instagram Reel (Video) ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/1 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "instagram_content_type": "reel", "description": "Morning routine with our skincare line", "target_publish_date": "2026-03-20", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/reel-skincare.mp4", "instagram_location": "Los Angeles, California", "instagram_collaborators": ["brandofficial"], "instagram_audio_name": "Original Audio", "instagram_add_to_story": true, "external_ref": "ig-reel-001" }' ``` ### Instagram Reel Fixed Photos (creates a video from images, not swipeable) ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/2 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "carousel", "instagram_content_type": "reel", "description": "Style inspiration for spring", "target_publish_date": "2026-03-20", "carousel_images": [ "carousel-images/org_xxx/bundle_def456/slide1.jpg", "carousel-images/org_xxx/bundle_def456/slide2.jpg" ], "instagram_location": "Paris, France", "instagram_collaborators": ["partner"], "instagram_add_to_story": true }' ``` ### Instagram Post Carousel (swipeable photo carousel) ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_def456/videos/3 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "carousel", "instagram_content_type": "post", "description": "Our latest collection", "target_publish_date": "2026-03-20", "carousel_images": [ "carousel-images/org_xxx/bundle_def456/photo1.jpg", "carousel-images/org_xxx/bundle_def456/photo2.jpg" ], "instagram_location": "Paris, France" }' ``` :::info Instagram Carousel Behavior For Instagram, `video_type: "carousel"` behaves differently depending on `instagram_content_type`: - With `"reel"`: creates a **video** from your images (Fixed Photos — not swipeable) - With `"post"`: creates a **swipeable photo carousel** ::: ### TikTok Video with Sound Volume Control Lower the original video sound and boost the added music. Costs 1 extra credit (charged once per video). ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1 \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "New product launch #ad", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/promo.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/trending-sound-789", "volume_original_sound": 20, "volume_added_sound": 150 }' ``` If your balance is below 1 credit: ```json { "error": { "code": "INSUFFICIENT_CREDITS", "message": "Not enough credits to complete this operation.", "details": { "reason": "Need 1 credit to enable sound volume control.", "feature": "sound_volume" } } } ``` --- :::tip Automatic Video Download You don't need to upload your video first. If you pass any **external URL** (Google Drive, Dropbox, any direct link) as `video_url`, the API will **automatically download the file and store it on our servers**. The returned `video_url` in the response will be the final hosted URL. This also works in batch configuration and CSV import. URLs already hosted on our storage (`pub-0d3f...r2.dev`) are kept as-is (no redundant re-upload). ::: --- ## Batch Configuration Configure multiple videos in a single request. The body must be wrapped in `{ "videos": [...] }`. ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/batch \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Day 1 of the challenge #ad", "target_publish_date": "2026-03-15", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/day1.mp4" }, { "position": 2, "video_type": "video", "description": "Day 2 — things are getting interesting #ad", "target_publish_date": "2026-03-18", "video_url": "https://pub-xxx.r2.dev/videos/bundle-id/day2.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/hype-beat-101" }, { "position": 3, "video_type": "carousel", "description": "Final results — swipe to see the transformation", "target_publish_date": "2026-03-21", "carousel_images": [ "carousel-images/org_xxx/bundle_abc123/before.jpg", "carousel-images/org_xxx/bundle_abc123/after.jpg" ], "tiktok_sound_url": "https://www.tiktok.com/music/reveal-sound-202" } ] }' ``` **Response:** ```json { "data": { "bundle_id": "bundle_abc123", "configured": 3, "errors": [], "results": [ { "position": 1, "video_type": "video", "status": "configured" }, { "position": 2, "video_type": "video", "status": "configured" }, { "position": 3, "video_type": "carousel", "status": "configured" } ] } } ``` If some videos fail validation, the response includes partial results: ```json { "data": { "bundle_id": "bundle_abc123", "configured": 2, "errors": [ { "position": 3, "error": "carousel_images is required when video_type is carousel" } ], "results": [ { "position": 1, "video_type": "video", "status": "configured" }, { "position": 2, "video_type": "video", "status": "configured" } ] } } ``` ## Date Validation The `target_publish_date` must meet the following minimum lead times: | Account Status | Minimum Lead Time | |---|---| | New (not yet delivered) | 3 days from today | | Existing (already delivered) | 1 day from today | If the date is too soon, the API returns a `VALIDATION_ERROR`: ```json { "error": { "code": "VALIDATION_ERROR", "message": "target_publish_date must be at least 3 days from today for new accounts.", "details": { "field": "target_publish_date", "minimum_date": "2026-02-14" } } } ``` ## Important: Media URL Types When referencing uploaded files in video configuration: - **`video_url`** — Use the `public_url` from the video upload response. - **`carousel_images`** — Use the `storage_path` from the image upload response (not `public_url`). - **`profile_picture_url`** — Use the `storage_path` from the image upload response (not `public_url`). See [Media Upload](/media-upload) for details on uploading files and obtaining URLs. --- ## CSV Import – Bulk Video Upload from Spreadsheet Source: api/04-videos/32-csv-import.md URL: https://developers.tokportal.com/csv-import # CSV Import Bulk-import videos into a bundle from a CSV file. The system parses each row, downloads video files from the provided URLs, uploads them to TokPortal storage, and configures the corresponding video positions automatically. ## Download Templates - **[TikTok CSV Template](/templates/tiktok-template.csv)** — Columns: position, name, video_type, video_url, carousel_images, description, target_publish_start_date, editing_instructions, tiktok_sound_url - **[Instagram CSV Template](/templates/instagram-template.csv)** — Columns: same as TikTok + instagram_content_type, instagram_location, instagram_collaborators, instagram_audio_name, instagram_add_to_story ## Import Videos from CSV ``` POST /bundles/:id/videos/import-csv ``` **Content-Type:** `multipart/form-data` Upload a `.csv` file as the `file` field. Each row maps to a video position in the bundle. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/import-csv \ -H "X-API-Key: sk_xxx" \ -F "file=@videos.csv" ``` ### Optional form fields | Field | Description | |---|---| | `auto_publish` | `"true"` or `"1"` to attempt `POST /bundles/:id/publish` after the import succeeds. The response will include an `auto_publish` object — see [auto_publish behaviour](/videos/configure-videos#auto_publish--configure-and-publish-in-one-call). Failures here never fail the import. | ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/import-csv \ -H "X-API-Key: sk_xxx" \ -F "file=@videos.csv" \ -F "auto_publish=true" ``` **Response:** ```json { "data": { "imported": 5, "skipped": 0, "errors": [] } } ``` If some rows fail validation, they are returned in the `errors` array while valid rows are still imported: ```json { "data": { "imported": 3, "skipped": 2, "errors": [ { "row": 4, "message": "target_publish_date must be at least 3 days from today." }, { "row": 5, "message": "carousel_images is required when video_type is carousel." } ] } } ``` ## CSV Column Reference | Column | Required | Description | |---|---|---| | `position` | Yes | Video position in the bundle (1-indexed). | | `video_type` | Yes | `video` or `carousel`. For Instagram: `carousel` + `reel` = Fixed Photos (video from images); `carousel` + `post` = swipeable Carousel. | | `description` | Yes | Caption or description for the post. | | `target_publish_date` | Yes | Publish date in `YYYY-MM-DD` format (full date strings also accepted). | | `video_url` | Conditional | URL to the video file. Required when `video_type` is `video`. Google Drive links are supported. | | `carousel_images` | Conditional | Semicolon-separated image URLs. Required when `video_type` is `carousel`. | | `tiktok_sound_url` | Conditional | TikTok sound URL. Required for TikTok carousels, optional for videos. | | `volume_original_sound` | No | Volume of the original video sound, integer `0`–`200` (percent). `100` = unchanged. Setting this for the first time on a video costs **1 credit**. | | `volume_added_sound` | No | Volume of the added sound/music, integer `0`–`200` (percent). `100` = unchanged. Setting this for the first time on a video costs **1 credit**. | | `editing_instructions` | No | Free-text instructions for the creator. | | `external_ref` | No | Your own reference ID for this video. | | `instagram_content_type` | Conditional | `reel` or `post`. Required for Instagram bundles. | | `instagram_location` | No | Location tag for Instagram posts. | | `instagram_collaborators` | No | Semicolon-separated Instagram usernames. | | `instagram_audio_name` | No | Audio track name for Instagram Reels. | | `instagram_add_to_story` | No | `true` or `false`. | | `youtube_title` | No | Reserved for future YouTube support. | | `youtube_tags` | No | Reserved for future YouTube support. | | `youtube_category` | No | Reserved for future YouTube support. | | `youtube_visibility` | No | Reserved for future YouTube support. | ## Video Download Behavior The system downloads video and image files from the URLs provided in the CSV and re-uploads them to TokPortal's own storage. This means: - **Google Drive links** are supported (public or shared links). - Direct download URLs from any publicly accessible host work. - After import, the `video_url` and `carousel_images` fields on the video will point to TokPortal storage URLs, not the originals. ## Formatting Rules - **Dates** — Use `YYYY-MM-DD` (e.g., `2026-03-15`) or a full ISO date string (e.g., `2026-03-15T00:00:00Z`). - **Multi-value fields** — Use semicolons (`;`) to separate multiple values in `carousel_images`, `instagram_collaborators`, and `youtube_tags`. - **Boolean fields** — Use `true` or `false` (case-insensitive). ## Sample CSV — TikTok ```csv position,video_type,description,target_publish_date,video_url,tiktok_sound_url,editing_instructions,external_ref 1,video,"Unboxing the new collection #ad",2026-03-15,https://drive.google.com/uc?id=abc123,https://www.tiktok.com/music/trending-789,"Add brand logo at the end",campaign-42-v1 2,video,"Trying the product for a week #sponsored",2026-03-18,https://drive.google.com/uc?id=def456,,,campaign-42-v2 3,carousel,"Top 5 looks from the drop",2026-03-21,,https://www.tiktok.com/music/chill-456,,campaign-42-v3 ``` For row 3 (carousel), you would also need a `carousel_images` column: ```csv position,video_type,description,target_publish_date,carousel_images,tiktok_sound_url,external_ref 3,carousel,"Top 5 looks from the drop",2026-03-21,"https://example.com/img1.jpg;https://example.com/img2.jpg;https://example.com/img3.jpg",https://www.tiktok.com/music/chill-456,campaign-42-v3 ``` ## Sample CSV — Instagram ```csv position,video_type,instagram_content_type,description,target_publish_date,video_url,instagram_location,instagram_collaborators,instagram_add_to_story,external_ref 1,video,reel,"Morning routine with our skincare ✨",2026-03-20,https://drive.google.com/uc?id=ghi789,"Los Angeles, California",brandofficial,true,ig-reel-001 2,carousel,post,"Summer collection — which is your fave?",2026-03-22,,"Miami, Florida",,false,ig-post-002 ``` --- ## Video Actions – Publish, Finalize & Corrections Source: api/04-videos/33-video-actions.md URL: https://developers.tokportal.com/video-actions # Video Actions These endpoints let you take actions on individual videos within a bundle — publish, finalize, request corrections, or unschedule. ## Publish a Video Publish a single configured video on an already-active bundle. Use this after adding new video slots to an accepted bundle, configuring them, and wanting the account manager to start working on them. ``` POST /bundles/:id/videos/:position/publish ``` The bundle must be in `published`, `published_priority`, or `accepted` status. The video must be in `configured` status. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/{id}/videos/3/publish \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "video_id": "uuid", "position": 3, "bundle_id": "uuid", "status": "published" } } ``` ## Publish All Configured Videos Publish all configured videos on a bundle at once. Useful after batch-configuring multiple new slots. ``` POST /bundles/:id/videos/publish-all ``` ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/{id}/videos/publish-all \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "bundle_id": "uuid", "videos_published": 3, "video_ids": ["uuid1", "uuid2", "uuid3"] } } ``` :::tip Typical workflow for adding videos to an active bundle 1. `POST /bundles/:id/add-video-slots` — Add new slots (credits debited) 2. `PUT /bundles/:id/videos/batch` — Configure the new videos 3. `POST /bundles/:id/videos/publish-all` — Publish them so the manager can start ::: ## Finalize a Video ``` POST /bundles/:id/videos/:position/finalize ``` Marks a video as finalized, completing its lifecycle. Once finalized, no further changes can be made to the video. A video can only be finalized when its current status is `accepted` or `in_review`. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/1/finalize \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "position": 1, "status": "finalized", "finalized_at": "2026-02-11T14:30:00Z" } } ``` **Error — invalid status transition:** ```json { "error": { "code": "INVALID_STATUS_TRANSITION", "message": "Cannot finalize a video with status 'configured'. The video must be 'accepted' or 'in_review'.", "details": { "current_status": "configured", "allowed_statuses": ["accepted", "in_review"] } } } ``` ## Request Corrections ``` POST /bundles/:id/videos/:position/corrections ``` Sends the video back to the creator with correction instructions. The video status reverts to `configured` so the creator can make changes and re-publish. | Field | Type | Required | Description | |---|---|---|---| | `message` | string | Yes | Instructions describing what needs to be changed. | ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/2/corrections \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "message": "The brand logo is not visible enough. Please make it larger and move it to the bottom-right corner." }' ``` **Response:** ```json { "data": { "position": 2, "status": "configured", "correction_requested_at": "2026-02-11T15:00:00Z", "correction_message": "The brand logo is not visible enough. Please make it larger and move it to the bottom-right corner." } } ``` ## Unschedule a Video ``` POST /bundles/:id/videos/:position/unschedule ``` Reverts a video back to `configured` status (cancels scheduling). Works on `published`, `configured`, or `accepted` videos. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/3/unschedule \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "video_id": "uuid", "position": 3, "bundle_id": "uuid", "status": "configured" } } ``` **Error — invalid status:** ```json { "error": { "code": "VIDEO_INVALID_STATUS", "message": "Invalid video status for this action.", "details": { "current_status": "finalized", "allowed": ["published", "configured", "accepted"], "hint": "Only published, configured, or accepted videos can be unscheduled." } } } ``` ## Reset a Video ``` POST /bundles/:id/videos/:position/reset ``` Clears all configuration from a video slot — description, URL, dates, sound, editing instructions, and all platform-specific fields — returning it to a blank state. The slot itself is preserved and can be reconfigured with `PUT /bundles/:id/videos/:position`. Only works on `pending` or `configured` videos. If a video is `published` or `accepted`, use **unschedule** first, then reset. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bundle_abc123/videos/3/reset \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "video_id": "uuid", "position": 3, "bundle_id": "uuid", "status": "configured", "message": "Video has been reset to blank state. You can reconfigure it with PUT /bundles/:id/videos/:position." } } ``` **Error — invalid status:** ```json { "error": { "code": "VIDEO_INVALID_STATUS", "details": { "current_status": "accepted", "allowed": ["pending", "configured"], "hint": "Only pending or configured videos can be reset. Use unschedule first if the video is published or accepted." } } } ``` :::tip To fully clear a published or accepted video 1. `POST /bundles/:id/videos/:position/unschedule` — reverts to `configured` 2. `POST /bundles/:id/videos/:position/reset` — clears all configuration ::: ## Action Summary | Action | Endpoint | Allowed From Status | Result Status | |---|---|---|---| | Publish | `POST .../publish` | `configured` | `published` | | Finalize | `POST .../finalize` | `accepted`, `in_review` | `finalized` | | Corrections | `POST .../corrections` | `published`, `accepted` | `configured` | | Unschedule | `POST .../unschedule` | `published`, `configured`, `accepted` | `configured` | | Reset | `POST .../reset` | `pending`, `configured` | *(blank, same status)* | | Fix Download | `POST .../fix-download` | any (requires `download_issue: true`) | *(same status, issue cleared)* | See also: [Fix Download Link](/videos/fix-download-link) for full details on detecting and fixing broken links. --- ## Fix Broken Video Download Links Source: api/04-videos/34-fix-download-link.md URL: https://developers.tokportal.com/fix-download-link # Fix Download Link When an account manager encounters a broken, expired, or inaccessible download link for a video, they flag it. The video's `download_issue` field becomes `true` and a `download_issue_comment` describes the problem reported by the manager. ## How It Works 1. **Manager flags a broken link** — the video gets `download_issue: true` with a comment describing the issue 2. **You detect the flag** — poll your videos via `GET /bundles/:id/videos` and check for `download_issue: true` 3. **You provide a new URL** — call `POST /bundles/:id/videos/:position/fix-download` with a working link 4. **Flag is cleared** — the manager can proceed with the video ## Detecting Broken Links Every video response includes two fields related to download issues: | Field | Type | Description | |---|---|---| | `download_issue` | boolean | `true` if the manager flagged a broken link | | `download_issue_comment` | string \| null | The manager's description of the problem | These fields are returned by: - `GET /bundles/:id/videos` (list all videos) - `GET /bundles/:id/videos/:position` (get single video) ### Example: video with a broken link ```json { "data": { "position": 2, "status": "configured", "video_url": "https://expired-cdn.example.com/old-video.mp4", "download_issue": true, "download_issue_comment": "Link returns 403 Forbidden. Please provide a new URL." } } ``` ## Fix a Broken Link ``` POST /bundles/:id/videos/:position/fix-download ``` Provide a new working URL to replace the broken one. The video file is downloaded and re-uploaded to TokPortal's storage, then the `download_issue` flag is cleared and the **target publish date is automatically reset to tomorrow** (today + 1 day) so the account manager is not penalized for the delay. :::info Date reset When you fix a broken link, the video's `target_publish_date` is automatically moved to tomorrow and the `target_publish_end_date` to the day after. This ensures the manager has a fresh deadline and isn't marked as late because of a broken link issue. ::: ### Request Body | Field | Required | Description | |---|---|---| | `video_url` | Conditional | New video URL (for `video` type). Provide this **or** `carousel_images`. | | `carousel_images` | Conditional | New carousel image URLs (for `carousel` type). Provide this **or** `video_url`. | **For a regular video:** ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/{bundle_id}/videos/2/fix-download \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://cdn.example.com/fixed-video.mp4" }' ``` **For a carousel:** ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/{bundle_id}/videos/2/fix-download \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "carousel_images": [ "https://cdn.example.com/slide1.jpg", "https://cdn.example.com/slide2.jpg" ] }' ``` ### Response (200) ```json { "data": { "id": "uuid", "position": 2, "status": "configured", "video_type": "video", "name": "Video 2", "video_url": "https://pub-xxx.r2.dev/videos/bundle_id/new-file.mp4", "download_issue": false, "download_issue_comment": null, "target_publish_date": "2026-02-22T00:00:00Z", "target_publish_end_date": "2026-02-23T00:00:00Z", "updated_at": "2026-02-21T14:00:00Z" } } ``` ### Errors | Code | Status | When | |---|---|---| | `BUNDLE_NOT_FOUND` | 404 | Bundle ID not found | | `BUNDLE_NOT_OWNED` | 403 | Bundle belongs to another user | | `VIDEO_NOT_FOUND` | 404 | No video at this position | | `VIDEO_POSITION_OUT_OF_RANGE` | 400 | Position exceeds `videos_quantity` | | `VIDEO_NO_DOWNLOAD_ISSUE` | 409 | The video does not have a download issue flagged — nothing to fix | | `UPLOAD_FAILED` | 500 | Failed to download/upload the replacement file | ## Uploading a File Instead of a URL If you want to upload a video file directly instead of providing a URL, use the standard upload endpoints first: 1. **Presigned upload:** `POST /upload/video` → get `upload_url` and `public_url` → PUT the file → use `public_url` in fix-download 2. **Direct upload:** `POST /upload/video/direct` (multipart) → get `public_url` → use it in fix-download See [Media Upload](/media-upload) for details. :::tip Automation tip Set up a periodic check (e.g. every 30 minutes) that calls `GET /bundles/:id/videos` on your active bundles and filters for `download_issue: true`. This lets you detect and fix broken links quickly, avoiding delays in your campaign. ::: --- ## Delivered Accounts – Credentials & Verification Codes Source: api/05-accounts/40-saved-accounts.md URL: https://developers.tokportal.com/saved-accounts # Accounts (Delivered) Once a bundle is accepted and the account manager creates the social media account, it becomes a **saved account** (also called a delivered account). These accounts contain full credentials and are available for you to manage. Accounts are **read-only** via the API. You can list them, view their details and credentials, and retrieve verification codes, but you cannot modify account data through the API. :::warning Support disclaimer on credential access Retrieving an account's password (via `POST /accounts/:id/reveal-credentials`) or a verification code (via `POST /accounts/:id/verification-code`) is typically the first step toward logging in to the account outside TokPortal. Doing so from a country other than the target country, with a VPN, or from a different IP address poses real risks to the account and its reach. We strongly recommend using **only TokPortal** to manage delivered accounts. **The first time you call either of these endpoints for a given account, you acknowledge that TokPortal will no longer provide support on that account and will not be able to respond to refund or modification requests.** You will still be able to order videos for the account and use it on TokPortal, but support in case of issues can no longer be guaranteed. The same disclaimer is shown in the dashboard before the password can be revealed or a verification code retrieved. The acknowledgment is per-account and only set once. Endpoints that only list or return non-credential metadata (e.g. `GET /accounts`, `GET /accounts/:id`, `GET /accounts/:id/bundles`) do **not** trigger this acknowledgment. ::: ## List Accounts ``` GET /accounts ``` Returns a paginated list of your delivered accounts. Use query parameters to filter results. | Parameter | Type | Description | |---|---|---| | `platform` | string | Filter by platform: `tiktok`, `instagram`, or `youtube`. | | `country` | string | *(Optional)* Filter by country code or TokPortal canonical country code (e.g., `US`, `USA`, `FR`, `UK`). | | `page` | integer | Page number (default: `1`). | | `per_page` | integer | Results per page (default: `25`, max: `100`). | :::tip The `country` parameter is optional. If you just need all your accounts, omit it — or filter by `platform` only. ::: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts?platform=tiktok" \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": [ { "id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "platform": "tiktok", "username": "coolcreator99", "visible_name": "Cool Creator", "biography": "Lifestyle & trends | DM for collabs", "profile_picture_url": "https://pub-xxx.r2.dev/profile-pictures/pfp-abc123.jpg", "country": "US", "link_in_bio": "https://linktr.ee/coolcreator99", "profile_url": "https://www.tiktok.com/@coolcreator99", "tokmail_email": "coolcreator99@tokmail.io", "created_at": "2026-01-10T08:00:00Z", "updated_at": "2026-02-05T12:30:00Z" }, { "id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "platform": "tiktok", "username": "trendwatcher22", "visible_name": "Trend Watcher", "biography": "Following the latest trends", "profile_picture_url": null, "country": "US", "link_in_bio": null, "profile_url": "https://www.tiktok.com/@trendwatcher22", "tokmail_email": "trendwatcher22@tokmail.io", "created_at": "2026-01-12T10:30:00Z", "updated_at": "2026-01-12T10:30:00Z" } ], "pagination": { "page": 1, "per_page": 20, "total": 2, "total_pages": 1 } } ``` ### List Response Fields | Field | Type | Description | |---|---|---| | `id` | string (UUID) | Unique account identifier. | | `platform` | string | `tiktok`, `instagram`, or `youtube`. | | `username` | string | Platform username. | | `visible_name` | string | Display name on the platform. | | `biography` | string | Account bio text. | | `profile_picture_url` | string \| null | URL to the profile picture. | | `country` | string | Country code returned by TokPortal. | | `link_in_bio` | string \| null | Link in bio URL. | | `profile_url` | string | Direct URL to the social media profile. | | `tokmail_email` | string | TokMail email address for this account. | | `created_at` | string | ISO 8601 creation timestamp. | | `updated_at` | string | ISO 8601 last-updated timestamp. | > **Note:** The list response does **not** include a `status` field. ## Get Account Details ``` GET /accounts/:id ``` Returns account metadata. **Credentials are not returned by this endpoint** — call `POST /accounts/:id/reveal-credentials` to obtain them. A plain metadata fetch does not trigger the support disclaimer. ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "platform": "tiktok", "username": "coolcreator99", "visible_name": "Cool Creator", "biography": "Lifestyle & trends | DM for collabs", "profile_picture_url": "https://pub-xxx.r2.dev/profile-pictures/pfp-abc123.jpg", "country": "US", "link_in_bio": "https://linktr.ee/coolcreator99", "profile_url": "https://www.tiktok.com/@coolcreator99", "credentials": null, "last_verification_code": null, "current_cm_id": "cm_abc123", "created_at": "2026-01-10T08:00:00Z", "updated_at": "2026-02-05T12:30:00Z", "reveal_endpoint": "/api/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials", "verification_code_endpoint": "/api/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code", "remade": { "was_remade": true, "remade_count": 1, "old_username": "coolcreator9", "remade_at": "2026-02-04T09:15:00Z" } } } ``` ### Additional Detail Fields | Field | Type | Description | |---|---|---| | `credentials` | null | Always `null`. POST `/accounts/:id/reveal-credentials` to obtain the password and social credentials. | | `last_verification_code` | null | Always `null`. POST `/accounts/:id/verification-code` to retrieve a fresh code. | | `current_cm_id` | string \| null | The ID of the account manager currently assigned. | | `reveal_endpoint` | string | Path to POST to in order to reveal credentials (triggers the support disclaimer). | | `verification_code_endpoint` | string | Path to POST to in order to retrieve the latest verification code. | | `remade` | object | Whether this account was rebuilt (remade) after the previous account was banned or lost. See [Remake info](#remake-info) below. | > **Note:** The detail response does **not** include `bundle_id` or `status`. #### Remake info When an account is banned or lost, TokPortal can **remake** it: the same bundle is republished and a fresh social account is created (usually under a slightly altered username). The `remade` object lets you detect this and recover the previous handle. | Field | Type | Description | |---|---|---| | `was_remade` | boolean | `true` if this account was remade at least once, otherwise `false`. | | `remade_count` | integer | Number of times the underlying bundle has been remade (`0` when never remade). | | `old_username` | string \| null | The previous username, captured at the time of the most recent remake. `null` if unknown or never remade. | | `remade_at` | string (ISO 8601) \| null | Timestamp of the most recent remake. `null` if never remade. | > **Note:** `remade` is always present on the detail response. When the account was never remade it returns `{ "was_remade": false, "remade_count": 0, "old_username": null, "remade_at": null }`. It is **not** included in the list response (`GET /accounts`). ## List Bundles for an Account ``` GET /accounts/:id/bundles ``` Returns all bundles associated with a delivered account. This includes bundles where the account was originally created (`account_and_videos`) and any `videos_only` bundles linked to this account. Each bundle includes a video slot summary, making it easy to find bundles with available posting slots. | Parameter | Type | Description | |---|---|---| | `status` | string | *(Optional)* Filter by bundle status: `pending_setup`, `published`, `accepted`, `completed`. | | `page` | integer | Page number (default: `1`). | | `per_page` | integer | Results per page (default: `25`, max: `100`). | ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?status=accepted" \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": [ { "id": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "title": "US TikTok Campaign Q1", "bundle_type": "account_and_videos", "platform": "tiktok", "country": "USA", "status": "accepted", "videos_quantity": 10, "edits_quantity": 2, "used_edits": 1, "credit_cost": 50, "external_ref": "campaign-q1-2026", "existing_account_id": null, "auto_finalize_videos": true, "wants_niche_warming": true, "wants_deep_warming": false, "wants_moderation": false, "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-02-01T14:30:00Z", "videos": { "total": 10, "pending": 3, "configured": 2, "published": 5, "accepted": 0, "finalized": 0 } } ], "pagination": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 } } ``` :::tip Finding available posting slots Use the `videos.pending` count to identify bundles that have empty video slots ready to be configured. This is especially useful when managing many bundles for the same account — you can quickly find where to schedule your next video without paginating through all bundles. ::: ## Retrieve Verification Code ``` POST /accounts/:id/verification-code ``` Retrieves the most recent 6-digit verification code from the account's TokMail inbox. :::warning Calling this endpoint triggers the support disclaimer described at the top of this page. See [Support disclaimer on credential access](/accounts/saved-accounts). ::: **How it works:** 1. Trigger a verification code from the platform (e.g., request a login code from TikTok or Instagram). 2. Wait a few seconds for the email to arrive. 3. Call this endpoint to retrieve the code from the TokMail inbox. ```bash curl -X POST https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "verification_code": "482937", "source": "email" } } ``` ### Response Fields | Field | Type | Description | |---|---|---| | `account_id` | string (UUID) | The account this code belongs to. | | `verification_code` | string | The 6-digit verification code. | | `source` | string | `"email"` (fetched from inbox) or `"cached"` (returned from cache). | **Error — no code found:** ```json { "error": { "code": "VERIFICATION_CODE_NOT_FOUND", "message": "No verification code found in the inbox. Make sure you triggered the code from the platform first and wait a few seconds before retrying." } } ``` > **Tip:** If you receive a `VERIFICATION_CODE_NOT_FOUND` error, wait 5-10 seconds and retry. Email delivery can take a moment. --- ## Account Edit Request – Profile Updates via API Source: api/05-accounts/41-account-edit-request.md URL: https://developers.tokportal.com/account-edit-request # Account Edit Request Request a profile edit on a delivered (saved) account. The assigned account manager will update the username, display name, biography, and/or profile picture as specified. **Cost:** 8 credits per edit request. ## Create Edit Request ``` POST /accounts/:id/edit-request ``` Submit a request to change one or more profile fields on a delivered account. The account must have an active order with an assigned account manager. ### Prerequisites - The account must have an **active order** with an assigned manager - No other edit request can be **in progress** for the same account - You need at least **8 credits** in your balance ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `requested_username` | string | Yes | New username (1–24 characters) | | `requested_visible_name` | string | Yes | New display name (1–30 characters) | | `requested_biography` | string | No | New biography (max 80 characters) | | `requested_profile_picture_url` | string | No | URL of the new profile picture | | `requested_link_in_bio` | string | No | Link in bio (Instagram only, must be a valid URL) | ```bash curl -X POST https://app.tokportal.com/api/ext/accounts/{account_id}/edit-request \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "requested_username": "new_username", "requested_visible_name": "New Display Name", "requested_biography": "Updated bio text", "requested_profile_picture_url": "https://example.com/new-photo.jpg" }' ``` ### Response (201) ```json { "data": { "edit_request_id": "uuid", "account_id": "uuid", "status": "pending", "requested_username": "new_username", "requested_visible_name": "New Display Name", "requested_biography": "Updated bio text", "requested_profile_picture_url": "https://example.com/new-photo.jpg", "requested_link_in_bio": null, "created_at": "2026-02-25T12:00:00Z" }, "credits_charged": 8, "credits_remaining": 42 } ``` ### Errors | Code | Status | When | |---|---|---| | `SAVED_ACCOUNT_NOT_FOUND` | 404 | Account ID not found | | `SAVED_ACCOUNT_NOT_OWNED` | 403 | Account belongs to another user | | `EDIT_REQUEST_NO_CM` | 400 | No manager assigned (no active order) | | `EDIT_REQUEST_ALREADY_EXISTS` | 409 | Another edit request is already in progress | | `EDIT_REQUEST_NO_ACTIVE_ORDER` | 400 | No active order for this account | | `INSUFFICIENT_CREDITS` | 402 | Not enough credits (need 8) | ## List Edit Requests ``` GET /accounts/:id/edit-request ``` Returns all edit requests for the given account, newest first. ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/{account_id}/edit-request \ -H "X-API-Key: sk_xxx" ``` ### Response (200) ```json { "data": [ { "id": "uuid", "status": "pending", "requested_username": "new_username", "requested_visible_name": "New Display Name", "requested_biography": "Updated bio", "requested_profile_picture_url": null, "requested_link_in_bio": null, "feedback": null, "credits_cost": 8, "created_at": "2026-02-25T12:00:00Z", "updated_at": "2026-02-25T12:00:00Z", "finalized_at": null } ] } ``` ## Edit Request Statuses | Status | Meaning | |---|---| | `pending` | Waiting for the manager to start | | `in_progress` | Manager is working on it | | `in_review` | Manager submitted changes, waiting for your review | | `finalized` | Completed successfully | :::tip You can poll `GET /accounts/:id/edit-request` to track the progress of your request. Once the status reaches `in_review`, you can review the changes made by the manager. ::: --- ## Analytics API – Analytics v2 Source: api/06-analytics/50-analytics.md URL: https://developers.tokportal.com/analytics # Analytics TokPortal Analytics v2 exposes account, portfolio, post-level, time-series, comment, export, and raw provider analytics for delivered accounts. Data is owner-scoped by API key and can come from multiple sources: - connected account analytics when the account is connected through TokPortal's supported social integrations - public fallback analytics when connected analytics are unavailable - TokPortal historical cache and post metadata All examples use the public API base URL: ```text https://app.tokportal.com/api/ext ``` ## Access Levels Analytics responses are plan-aware: | Level | Access | |---|---| | Starter | Basic totals and limited post/account visibility. | | Creator | Core performance, time series, CSV export, and web reports. | | Marketer/Farmer+ | Full analytics, raw provider payloads, demographics, comments, and advanced post intelligence where available. | Every Analytics v2 response includes contract metadata or the `X-TokPortal-Analytics-Contract` header. --- ## Portfolio Dashboard ```http GET /analytics ``` Returns the Analytics v2 dashboard for the API key owner: totals, accounts, top posts, audience summary, facets, access metadata, and the current data contract. Optional filters: | Query | Description | |---|---| | `workspace` | Workspace ID. | | `platform` | Repeatable platform filter, e.g. `platform=tiktok&platform=instagram`. | | `country` | Repeatable country filter. | | `account` | Repeatable saved account ID filter. | | `from` | Start date or ISO timestamp. | | `to` | End date or ISO timestamp. | ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics?platform=tiktok" \ -H "X-API-Key: sk_xxx" ``` Response shape: ```json { "tier": "full", "access": { "level": "full", "planTier": "farmer", "reason": "full_access" }, "totals": { "account_count": 99, "views": 648356, "impressions": 619676, "tracked_posts": 4876, "post_views": 2181245, "avg_fyp_pct": null }, "accounts": [], "top_posts": [], "audience": { "age": [], "gender": [], "country": [], "city": [], "unit": "unknown" }, "contract": { "name": "tokportal.analytics.v2", "version": "2026-05-04.1", "accessLevel": "full" } } ``` --- ## Account Drilldown ```http GET /analytics/accounts/:id ``` Returns one account's Analytics v2 drilldown: account header, latest account snapshot, posts, daily series, demographics, derived stats, intelligence, and contract metadata. ```bash curl -X GET https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c \ -H "X-API-Key: sk_xxx" ``` Important fields: | Field | Description | |---|---| | `latest` | Latest account-level metrics. | | `posts` | Tracked post list with latest post snapshot metrics. | | `daily_series` | Stored daily points for charts. | | `demographics` | Parsed account or post audience demographics when available. | | `derived_stats` | Derived lifetime stats from tracked posts. | | `intelligence` | Cached content/account intelligence when available. | --- ## Time Series ```http GET /analytics/series ``` Returns stored chart points. This endpoint does not live-refresh data. Query parameters: | Query | Description | |---|---| | `metric` | `views`, `likes`, `comments`, `shares`, or `followers`. Default: `views`. | | `granularity` | `day` or `week`. Default: `day`. | | `mode` | `cumulative`, `gained`, or `snapshot`. Default: `cumulative`. | | `account` | Repeatable saved account ID. | | `from` | Start date or ISO timestamp. | | `to` | End date or ISO timestamp. | ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/series?metric=views&granularity=day&mode=cumulative&account=9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c&from=2026-05-01&to=2026-05-10" \ -H "X-API-Key: sk_xxx" ``` --- ## Account Compatibility Endpoint ```http GET /accounts/:id/analytics ``` This older public path now uses Analytics v2. It keeps legacy-shaped fields for existing integrations and also exposes the full v2 payload under `data.analytics_v2`. ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics \ -H "X-API-Key: sk_xxx" ``` Response: ```json { "data": { "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "followers_count": 12400, "total_videos_tracked": 48, "total_views": 580000, "total_likes": 42000, "average_engagement_rate": 7.24, "analytics_version": "v2", "analytics_v2": { "account": {}, "latest": {}, "posts": [] } } } ``` --- ## Post Analytics by Account ```http GET /accounts/:id/analytics/videos ``` Returns Analytics v2 tracked posts in the older paginated list shape. Query parameters: | Parameter | Type | Default | Description | |---|---|---|---| | `sort_by` | string | `upload_date` | `views`, `likes`, `engagement_rate`, or `upload_date`. | | `sort_order` | string | `desc` | `asc` or `desc`. | | `page` | integer | `1` | Page number. | | `per_page` | integer | `50` | Results per page, max `100`. | ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?sort_by=views&sort_order=desc" \ -H "X-API-Key: sk_xxx" ``` Rows include `tracked_post_id`, `analytics_version: "v2"`, and the full v2 row under `analytics_v2`. --- ## Single Post Analytics ```http GET /videos/:id/analytics ``` Pass an Analytics v2 `tracked_post_id` when available. For older integrations, this endpoint can still fall back to a legacy `video_analytics.id`. ```bash curl -X GET https://app.tokportal.com/api/ext/videos/8c2d0f2e-.../analytics \ -H "X-API-Key: sk_xxx" ``` Response fields include normalized post metrics, `analytics_version`, and for v2 records an `analytics_v2` object with the tracked post and latest snapshot. --- ## Comment Pulse ```http GET /analytics/comments ``` Returns a dashboard-level comment pulse across top posts in the selected scope. Full-tier only. The endpoint serves cached comments and cached semantic analysis, hydrating comments only when the cache is empty or stale. Query parameters: | Parameter | Description | |---|---| | `limit` | Total comments to return, `1..40`. Default: `24`. | | `postLimit` | Number of top posts to sample, `1..10`. Default: `6`. | | `commentsPerPost` | Comments to hydrate per post when needed, `1..12`. Default: `8`. | | `post` | Repeatable tracked post ID to force a specific sample. | | `workspace` | Workspace ID. | | `account` | Repeatable saved account ID. | | `platform` | Repeatable platform filter. | | `country` | Repeatable country filter. | | `from` | Start date or ISO timestamp. | | `to` | End date or ISO timestamp. | ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/comments?platform=tiktok&limit=24" \ -H "X-API-Key: sk_xxx" ``` --- ## Post Comments ```http GET /analytics/accounts/:id/comments ``` Returns cached comments for one tracked post. Full-tier only. Query parameters: | Parameter | Description | |---|---| | `trackedPostId` | Analytics v2 tracked post ID. | | `postId` | Connected provider post ID fallback. | | `limit` | Number of comments, max `50`. | ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=8c2d0f2e-..." \ -H "X-API-Key: sk_xxx" ``` --- ## CSV Export ```http GET /analytics/export/videos ``` Returns `text/csv` for all videos in scope. The exported columns are plan-aware. Optional filters: | Query | Description | |---|---| | `workspace` | Workspace ID. | | `account` | Repeatable saved account ID. | | `platform` | Repeatable platform filter. | | `country` | Repeatable country filter. | | `q` | Search text over account, post text, URLs, and platform IDs. | | `from` | Start date or ISO timestamp. | | `to` | End date or ISO timestamp. | ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/export/videos?account=9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_xxx" ``` --- ## HTML Reports ```http POST /analytics/export/reports ``` Creates shareable Analytics v2 web reports. Creator and full-tier plans can generate reports. Custom branding is available for full/farmer plans. ```bash curl -X POST https://app.tokportal.com/api/ext/analytics/export/reports \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "title": "April performance report", "accountIds": ["9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"], "from": "2026-04-01", "to": "2026-04-30" }' ``` The response includes a report `token` and public `url`. --- ## Raw Provider Payloads Full-tier clients can retrieve stored raw provider payloads. These endpoints are useful when you need fields that TokPortal has stored but has not yet normalized into product UI fields. ### Account Raw Snapshots ```http GET /analytics/accounts/:id/raw ``` Returns rows from `account_analytics_snapshots`, including normalized metrics and the stored `raw` JSON payload. ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=bundle_social&limit=3" \ -H "X-API-Key: sk_xxx" ``` ### Post Raw Snapshots ```http GET /analytics/posts/:tracked_post_id/raw ``` Returns rows from `post_analytics_snapshots`, including normalized metrics, raw per-post analytics, and tracked post metadata. ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/posts/8c2d0f2e-.../raw?source=bundle_social&limit=3" \ -H "X-API-Key: sk_xxx" ``` Raw endpoint filters: | Query | Description | |---|---| | `source` | `bundle_social`, `apify`, or `manual`. | | `limit` | `1..30`, default `5`. | | `from` | Start date or ISO timestamp. | | `to` | End date or ISO timestamp. | --- ## Refresh Analytics ```http POST /accounts/:id/analytics/refresh ``` Triggers a targeted Analytics v2 refresh for one account. Background sync is the primary refresh path; use this endpoint for targeted repair or bootstrap flows. Body: ```json { "includePosts": true, "includeComments": true, "postLimit": 20, "force": false, "forcePosts": false, "bootstrapPosts": false } ``` The server dedupes repeated requests: | Source | Dedupe window | |---|---| | Connected analytics | 6 hours | | Public fallback analytics | 24 hours | Response: ```json { "data": { "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "platform": "tiktok", "status": "refreshed", "analytics_version": "v2" } } ``` --- ## Check Refresh Availability ```http GET /accounts/:id/analytics/can-refresh ``` Returns whether targeted refresh is currently available for an account under the Analytics v2 dedupe policy. ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh \ -H "X-API-Key: sk_xxx" ``` Response: ```json { "data": { "account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "can_refresh": true, "next_refresh_at": null, "hours_remaining": 0, "source": "connected", "analytics_version": "v2" } } ``` --- ## Contract ```http GET /analytics/contract ``` Returns the global Analytics v2 contract, the current API key's access payload, metric semantics, endpoint metadata, freshness targets, and redaction rules. --- ## Media Upload – Videos & Images via Presigned URLs Source: api/07-media-upload/60-media-upload.md URL: https://developers.tokportal.com/media-upload # Media Upload Before configuring videos, you need to upload your media files (videos and images) to TokPortal's storage. You have **two options**: - **Direct upload** (simpler) — Send the file directly to the API. Best for scripts, MCP, and automation. - **Presigned URL** (advanced) — Get a temporary upload URL and PUT the file yourself. Best for browser-based uploads. --- ## Option 1: Direct Upload Upload a file directly — the server handles everything. ### Upload Video (Direct) ``` POST /upload/video/direct Content-Type: multipart/form-data ``` ```bash curl -X POST https://app.tokportal.com/api/ext/upload/video/direct \ -H "X-API-Key: sk_xxx" \ -F "file=@/path/to/video.mp4" \ -F "bundle_id=your-bundle-id" ``` **Response:** ```json { "data": { "public_url": "https://pub-xxx.r2.dev/videos/bundle-id/123456.mp4", "filename": "video.mp4", "size_bytes": 15234567, "content_type": "video/mp4" } } ``` ### Upload Image (Direct) ``` POST /upload/image/direct Content-Type: multipart/form-data ``` ```bash curl -X POST https://app.tokportal.com/api/ext/upload/image/direct \ -H "X-API-Key: sk_xxx" \ -F "file=@/path/to/photo.jpg" \ -F "bundle_id=your-bundle-id" \ -F "purpose=carousel" ``` `purpose` can be `carousel` (default) or `profile_picture`. **Response:** ```json { "data": { "public_url": "https://xxx.supabase.co/storage/v1/object/public/carousel-images/...", "storage_path": "carousel-images/org_xxx/bundle-id/photo-123456.jpg", "filename": "photo.jpg", "size_bytes": 245678, "content_type": "image/jpeg" } } ``` ### Using Upload Results in Video Configuration | Video Config Field | Use This Value | |---|---| | `video_url` | `public_url` from the **video** upload response | | `carousel_images[]` | `storage_path` from the **image** upload response | | `profile_picture_url` | `storage_path` from the **image** upload response | > **Important:** For `carousel_images` and `profile_picture_url`, use `storage_path` — **not** `public_url`. For `video_url`, use `public_url`. --- ## Import Image From URL If your image already exists at a public direct URL, TokPortal can fetch and store it permanently. ```http POST /upload/image/from-url Content-Type: application/json ``` ```bash curl -X POST https://app.tokportal.com/api/ext/upload/image/from-url \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "url": "https://cdn.example.com/slide1.jpg", "bundle_id": "your-bundle-id", "purpose": "carousel" }' ``` Use the returned `storage_path` for `carousel_images` or `profile_picture_url`. --- ## Option 2: Presigned URL Upload Get a temporary URL and upload the file yourself. This is useful for browser-based uploads where the client uploads directly. ### Upload Flow 1. **Request a presigned URL** — Call the upload endpoint with file metadata. 2. **Upload the file** — Use the returned `upload_url` and `method` to upload the file. 3. **Use the URL in video config** — Pass the appropriate URL to the video configuration endpoints. --- ## Presigned Video Upload ``` POST /upload/video ``` Returns a presigned Cloudflare R2 URL for uploading a video file. | Field | Type | Required | Description | |---|---|---|---| | `filename` | string | Yes | Original filename including extension (e.g., `promo.mp4`). | | `content_type` | string | Yes | MIME type of the file (e.g., `video/mp4`). | | `bundle_id` | string | Yes | The bundle this video belongs to. | ### Step 1 — Get the presigned URL ```bash curl -X POST https://app.tokportal.com/api/ext/upload/video \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "filename": "promo.mp4", "content_type": "video/mp4", "bundle_id": "bnd_a1b2c3d4" }' ``` **Response:** ```json { "data": { "upload_url": "https://storage.tokportal.com/videos/org_xxx/bnd_a1b2c3d4/promo-1707660000.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&...", "public_url": "https://pub-xxx.r2.dev/videos/org_xxx/bnd_a1b2c3d4/promo-1707660000.mp4", "method": "PUT", "content_type": "video/mp4", "expires_in_seconds": 3600, "instructions": "PUT the file to upload_url with the specified Content-Type header." } } ``` ### Step 2 — Upload the file Use the `method` and `upload_url` from the response: ```bash curl -X PUT "https://storage.tokportal.com/videos/org_xxx/bnd_a1b2c3d4/promo-1707660000.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&..." \ -H "Content-Type: video/mp4" \ --data-binary @promo.mp4 ``` ### Step 3 — Use the public URL Pass `public_url` as `video_url` when [configuring a video](/videos/configure-videos): ```json { "video_type": "video", "video_url": "https://pub-xxx.r2.dev/videos/org_xxx/bnd_a1b2c3d4/promo-1707660000.mp4", "description": "New product launch", "target_publish_date": "2026-03-15" } ``` --- ## Presigned Image Upload ``` POST /upload/image ``` Returns a presigned Supabase Storage URL for uploading an image. Use this for carousel slides and profile pictures. | Field | Type | Required | Description | |---|---|---|---| | `filename` | string | Yes | Original filename including extension (e.g., `slide1.jpg`). | | `content_type` | string | Yes | MIME type of the file (e.g., `image/jpeg`). | | `bundle_id` | string | Yes | The bundle this image belongs to. | | `purpose` | string | No | `"carousel"` (default) or `"profile_picture"`. | ### Step 1 — Get the presigned URL ```bash curl -X POST https://app.tokportal.com/api/ext/upload/image \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "filename": "slide1.jpg", "content_type": "image/jpeg", "bundle_id": "bnd_a1b2c3d4", "purpose": "carousel" }' ``` **Response:** ```json { "data": { "upload_url": "https://xxx.supabase.co/storage/v1/object/upload/sign/carousel-images/org_xxx/bnd_a1b2c3d4/slide1-1707660000.jpg?token=...", "public_url": "https://xxx.supabase.co/storage/v1/object/public/carousel-images/org_xxx/bnd_a1b2c3d4/slide1-1707660000.jpg", "storage_path": "carousel-images/org_xxx/bnd_a1b2c3d4/slide1-1707660000.jpg", "method": "PUT", "content_type": "image/jpeg", "expires_in_seconds": 3600, "instructions": "PUT the file to upload_url with the specified Content-Type header." } } ``` ### Step 2 — Upload the file ```bash curl -X PUT "https://xxx.supabase.co/storage/v1/object/upload/sign/carousel-images/org_xxx/bnd_a1b2c3d4/slide1-1707660000.jpg?token=..." \ -H "Content-Type: image/jpeg" \ --data-binary @slide1.jpg ``` ### Step 3 — Use the storage path For `carousel_images` and `profile_picture_url`, use the `storage_path` value (**not** `public_url`): ```json { "video_type": "carousel", "carousel_images": [ "carousel-images/org_xxx/bnd_a1b2c3d4/slide1-1707660000.jpg", "carousel-images/org_xxx/bnd_a1b2c3d4/slide2-1707660000.jpg" ], "description": "Swipe through our latest collection", "target_publish_date": "2026-03-15" } ``` --- ## Presigned Upload Response Fields | Field | Type | Description | |---|---|---| | `upload_url` | string | The temporary presigned URL to upload the file to. | | `public_url` | string | The permanent public URL for the file after upload. | | `storage_path` | string | The storage path (images only). Use this for `carousel_images` and `profile_picture_url`. | | `method` | string | HTTP method to use for the upload (always `PUT`). | | `content_type` | string | The MIME type to set in the upload `Content-Type` header. | | `expires_in_seconds` | integer | Seconds until the presigned URL expires (typically `3600`). | | `instructions` | string | Human-readable instructions for completing the upload. | --- ## Accepted Formats ### Video | Format | MIME Type | Extension | |---|---|---| | MP4 | `video/mp4` | `.mp4` | | MOV | `video/quicktime` | `.mov` | | WebM | `video/webm` | `.webm` | ### Image | Format | MIME Type | Extension | |---|---|---| | JPEG | `image/jpeg` | `.jpg`, `.jpeg` | | PNG | `image/png` | `.png` | | WebP | `image/webp` | `.webp` | ## Presigned URL Expiration Presigned URLs expire after the time indicated in `expires_in_seconds` (typically **1 hour** / 3600 seconds). If the URL expires before you upload, request a new one. ## Error Handling **Unsupported file type:** ```json { "error": { "code": "UNSUPPORTED_FILE_TYPE", "message": "The content type 'video/avi' is not supported.", "details": { "supported_types": ["video/mp4", "video/quicktime", "video/webm"] } } } ``` **Missing required field:** ```json { "error": { "code": "VALIDATION_ERROR", "message": "bundle_id is required." } } ``` --- ## MCP Server – AI Integration for Cursor & Claude Source: api/08-mcp/70-mcp.md URL: https://developers.tokportal.com/mcp import CursorButton from '@site/src/components/CursorButton'; # Model Context Protocol (MCP) ## Connect your AI tools to TokPortal using MCP The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is a standard for connecting Large Language Models (LLMs) to platforms like TokPortal. Once connected, your AI assistant can create bundles, configure accounts and videos, track analytics, and manage your entire TokPortal workflow — all through natural language. ## Quick Setup ### Step 1: Get an API key Generate an API key from the [Developer Portal](https://app.tokportal.com/developer/api-keys). Your key starts with `sk_` and is shown only once. ### Step 2: Configure your AI tool Or manually add to `.cursor/mcp.json`: ```json { "mcpServers": { "tokportal": { "command": "npx", "args": ["-y", "tokportal-mcp"], "env": { "TOKPORTAL_API_KEY": "sk_your_key_here" } } } } ``` #### Claude Desktop Add to your `claude_desktop_config.json`: ```json { "mcpServers": { "tokportal": { "command": "npx", "args": ["-y", "tokportal-mcp"], "env": { "TOKPORTAL_API_KEY": "sk_your_key_here" } } } } ``` #### Windsurf / Other MCP clients Use the same configuration format. The MCP server runs via `npx tokportal-mcp` — no installation required. ### Step 3: Verify the connection After setup, verify the connection by asking your AI assistant: > "What is my TokPortal credit balance? Use MCP tools." If connected, the assistant will call the `get_credit_balance` tool and return your current balance. ## Available Tools The TokPortal MCP server is generated from the public OpenAPI schema. The full generated surface currently exposes 78 operations, but most workflows only need a small subset: auth, credits, bundles, videos, webhooks, uploads, and analytics. ### Error Diagnostics When an API request fails, MCP tool output includes the original API payload plus diagnostics: ```json { "payload": { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded." } }, "diagnostics": { "request_id": "req_...", "retry_after_seconds": 1, "rate_limit": { "limit": 120, "remaining": 0, "reset": 1779724800 } } } ``` Use `request_id` when contacting support. Use `retry_after_seconds` before retrying a rate-limited tool call. | Tool | Description | | ---------------------------------------------- | ------------------------------------------------- | | `tokportal_get_current_user` | Get authenticated user | | `tokportal_list_countries` | List available countries | | `tokportal_list_platforms` | List available platforms | | `tokportal_get_credit_costs` | Get credit pricing | | `tokportal_get_credit_balance` | Get credit balance | | `tokportal_list_credit_transactions` | List credit transactions | | `tokportal_list_bundles` | List bundles | | `tokportal_create_bundle` | Create a bundle | | `tokportal_create_bundles_bulk` | Create bundles in bulk | | `tokportal_get_bundle` | Get a bundle | | `tokportal_publish_bundle` | Publish a bundle | | `tokportal_get_bundle_publish_readiness` | Check bundle publish readiness | | `tokportal_unpublish_bundle` | Unpublish a bundle | | `tokportal_add_video_slots` | Add video slots to a bundle | | `tokportal_add_edit_slots` | Add edit slots to a bundle | | `tokportal_get_bundle_account` | Get bundle account configuration | | `tokportal_configure_bundle_account` | Configure bundle account profile | | `tokportal_get_bundle_video` | Get video slot configuration | | `tokportal_configure_bundle_video` | Configure a video slot | | `tokportal_patch_bundle_video` | Patch video references | | `tokportal_batch_configure_bundle_videos` | Configure video slots in bulk | | `tokportal_publish_all_bundle_videos` | Publish all configured videos on an active bundle | | `tokportal_list_accounts` | List delivered accounts | | `tokportal_get_account` | Get a delivered account | | `tokportal_list_account_bundles` | List bundles for a delivered account | | `tokportal_retrieve_account_verification_code` | Retrieve latest account verification code | | `tokportal_reveal_account_credentials` | Reveal delivered account credentials | | `tokportal_get_account_edit_request` | Get active account edit request | | `tokportal_create_account_edit_request` | Request profile edits for a delivered account | | `tokportal_list_webhook_endpoints` | List webhook endpoints | | `tokportal_create_webhook_endpoint` | Create a webhook endpoint | | `tokportal_get_webhook_endpoint` | Get a webhook endpoint | | `tokportal_update_webhook_endpoint` | Update a webhook endpoint | | `tokportal_delete_webhook_endpoint` | Delete a webhook endpoint | | `tokportal_list_webhook_deliveries` | List webhook deliveries | | `tokportal_test_webhook_endpoint` | Send a test webhook | | `tokportal_get_analytics_dashboard` | Get analytics dashboard | | `tokportal_get_analytics_series` | Get analytics time series | | `tokportal_get_analytics_account` | Get account analytics drilldown | | `tokportal_get_account_analytics` | Get account analytics compatibility view | | `tokportal_list_account_video_analytics` | List post analytics for an account | | `tokportal_get_video_analytics` | Get single video analytics | | `tokportal_get_comment_pulse` | Get comment pulse analytics | | `tokportal_list_analytics_account_comments` | List comments for an account post | | `tokportal_list_comment_tasks` | List comment tasks | | `tokportal_create_comment_tasks` | Create comment tasks | | `tokportal_get_comment_task` | Get a comment task | | `tokportal_delete_comment_task` | Delete a comment task | | `tokportal_approve_comment_task` | Approve a manually confirmed comment task | | `tokportal_dispute_comment_task` | Dispute a manually confirmed comment task | | `tokportal_list_comment_task_verifications` | List comment task verification events | ## Usage Examples Once connected, you can use natural language to interact with TokPortal: **Create a campaign:** > "Create a TikTok bundle in the US with 10 videos and niche warming for gaming content" **Configure videos:** > "Configure videos 1-5 on my latest bundle with these descriptions and schedule them one day apart starting March 15" **Check analytics:** > "Show me the analytics for all my US TikTok accounts, sorted by engagement rate" **Manage workflow:** > "List all my published bundles and show me which ones have videos pending review" **Get verification codes:** > "Get the verification code for my account @mybrand_us" ## npm Package The MCP server is published as [`tokportal-mcp`](https://www.npmjs.com/package/tokportal-mcp) on npm. ```bash npx tokportal-mcp ``` No installation required — `npx` downloads and runs it automatically. ## Security - Your API key is stored locally in your MCP configuration file and is **never sent anywhere except to the TokPortal API**. - The MCP server runs locally on your machine as a stdio process. - All communication with TokPortal uses HTTPS. - We recommend using separate API keys for MCP and other integrations. - You can revoke API keys at any time from the [Developer Portal](https://app.tokportal.com/developer/api-keys). --- ## OpenClaw Skill – AI Social Media Automation Source: api/09-openclaw/80-openclaw.md URL: https://developers.tokportal.com/openclaw # OpenClaw Integration TokPortal is available as an [OpenClaw skill](https://clawhub.ai/naybu256/tokportal) on ClawHub. Install it with one command and use the generated TokPortal MCP toolset via natural language. ## Install ```bash clawhub install tokportal ``` ## Configure Set your API key in `~/.openclaw/openclaw.json`: ```json { "skills": { "entries": { "tokportal": { "enabled": true, "apiKey": "sk_your_key_here" } } } } ``` Generate an API key from the [Developer Portal](https://app.tokportal.com/developer/api-keys). ## Use Start a new OpenClaw session and the TokPortal workflow tools become available via natural language: > "Create a TikTok bundle in the US with 10 videos" > "Show my credit balance" > "Configure video 1 with this description and schedule it for March 15" > "Get the verification code for my account @mybrand" All tools from the [MCP Server](/mcp) are available — bundles, accounts, videos, analytics, uploads, and more. ## Browse the skill [clawhub.ai/naybu256/tokportal](https://clawhub.ai/naybu256/tokportal) --- ## Comments – Overview & Lifecycle Source: api/10-comments/60-overview.md URL: https://developers.tokportal.com/comments-overview # 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. ``` 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 (refund 1 credit) 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 comment task** — debited up-front when the task is created. Refunded automatically if: - the row is rejected at validation time (e.g. invalid URL, account not yours) - you cancel a `pending` task via `DELETE /api/ext/comments/{id}` - the 72-hour hard deadline expires before the manager posts (auto-cancellation) The CM payout is paid out of TokPortal funds — it does **not** affect your credit balance. ## Supported platforms | Platform | Comment posting | Auto-verification | Comment max length | | ---------- | --------------- | ----------------- | ------------------ | | TikTok | ✅ | ✅ | 150 chars | | Instagram | ✅ | ✅ | 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). ## 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: ```json { "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 | Video URL platform ≠ account platform. | | `COMMENT_INVALID_VIDEO_URL` | 400 | Could not parse the video URL. | | `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. | | `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`/`404` error code. Easier to detect failure. - **Batch (`tasks: [...]`)** → always returns `201` with a `rejected` array 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: 1. Scrapes the target video's last ~100 comments via Apify. 2. Filters to comments whose author handle matches the saved account. 3. Computes Sørensen-Dice text similarity vs your `comment_text`. 4. 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) or `manually_confirmed` (your turn) You can fetch the full verifier timeline via [`GET /api/ext/comments/{id}/verifications`](./64-verifications.md). ## 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 `POST` body. - Hard deadline per task: **72 hours** between creation and `submitted`. Tasks that expire are auto-cancelled and credits refunded. --- ## Create Comments – Single & Batch Source: api/10-comments/61-create.md URL: https://developers.tokportal.com/create-comments # Create Comments ``` POST /api/ext/comments ``` Create one comment task or up to **200 in a single call**. Each task costs **1 credit**, debited up-front; rejected rows are refunded automatically. ## Single task Send a single object: ```bash curl -X POST https://app.tokportal.com/api/ext/comments \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://www.tiktok.com/@someone/video/7000000000000000000", "comment_text": "Love this technique ✨" }' ``` **Success (201):** ```json { "data": { "created": [ { "id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d", "status": "pending", "platform": "tiktok", "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://www.tiktok.com/@someone/video/7000000000000000000", "target_author_handle": "someone", "comment_text": "Love this technique ✨", "cm_payout_amount": 0.15, "submitted_at": null, "verified_at": null, "manually_confirmed_at": null, "client_dispute_deadline_at": null, "finalized_at": null, "correction_required": null, "deadline_at": "2026-04-30T17:21:00Z", "created_at": "2026-04-27T17:21:00Z", "updated_at": "2026-04-27T17:21:00Z" } ], "rejected": [] }, "created_count": 1, "rejected_count": 0, "credits_charged": 1 } ``` On a single-task call, **validation errors return a typed 4xx** instead of a `rejected` array, so you can branch on `error.code` without parsing the body twice. See [error codes](./60-overview.md#error-handling). ## Batch (up to 200) Wrap in `{ "tasks": [...] }`: ```bash curl -X POST https://app.tokportal.com/api/ext/comments \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "tasks": [ { "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://www.tiktok.com/@someone/video/7000000000000000000", "comment_text": "Brilliant idea" }, { "saved_account_id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", "target_video_url": "https://www.instagram.com/reel/AbCdEf12345/", "comment_text": "Stunning shot 🌟" }, { "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "ftp://broken.example", "comment_text": "Cool" } ] }' ``` **Partial-success response (201):** ```json { "data": { "created": [ { "id": "...", "status": "pending", "...": "..." }, { "id": "...", "status": "pending", "...": "..." } ], "rejected": [ { "reason": "invalid_video_url", "raw": { "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "ftp://broken.example" } } ] }, "created_count": 2, "rejected_count": 1, "credits_charged": 2 } ``` The batch path **always returns 201** — even when every row fails. Read `created_count` / `rejected_count` to drive your retry logic. ## Request body | Field | Type | Required | Description | | ------------------ | -------------- | -------- | -------------------------------------------------------------------------------------------- | | `saved_account_id` | string (UUID) | yes | A delivered account you own. Find IDs via `GET /api/ext/accounts`. | | `target_video_url` | string | yes | TikTok / Instagram video URL. The parser is lenient — protocol, `vm.tiktok.com/...`, `tiktok.com/t/...`, IG `/share/...`, `instagram.com/reel/...`, all work. | | `comment_text` | string | yes | The exact text the manager will post. Length capped per platform (TikTok 150, IG 2200). | | `brief_id` | string (UUID) | no | Optional internal grouping ID — see your dashboard. | For the batch form, wrap in `{ "tasks": [ ... ] }` (1–200 entries). ### Validation rules The same rules apply to single and batch: 1. `saved_account_id` must belong to your user (`SAVED_ACCOUNT_NOT_OWNED` otherwise). 2. The account must have an **active manager** (`current_cm_id IS NOT NULL`). If not, you'll see `COMMENT_ACCOUNT_NOT_MANAGED` (single) or `account_not_managed` (batch). Wait until an order is in progress on that account. 3. `target_video_url` must parse to one of the supported platforms. 4. The video's platform must match the account's platform — otherwise `COMMENT_PLATFORM_MISMATCH` (single) or `platform_mismatch` (batch). 5. `comment_text` length must be 1–150 (TikTok), 1–2200 (IG). ### Per-row rejection reasons (batch path) | `rejected[*].reason` | Meaning | | --------------------------------- | ----------------------------------------------------------------------------- | | `account_not_found` | `saved_account_id` doesn't exist. | | `account_not_owned` | Account belongs to a different user. | | `account_not_managed` | Account has no `current_cm_id` — no manager can take this task. | | `account_lookup_failed` | DB hiccup — retry the row. | | `invalid_video_url` | URL didn't parse. | | `platform_mismatch` | URL platform ≠ account platform. | | `comment_text_too_long` | Exceeded the platform's max chars. | | `comment_text_length_0_exceeds_X` | Text is empty (legacy literal). | | `insufficient_credits` | Wallet ran out mid-batch. Top up and retry the rejected rows. | ## Tips - **Idempotency:** if you must retry a batch, dedupe locally on `(saved_account_id, target_video_url, comment_text)` first — TokPortal does not deduplicate identical calls. The same comment posted twice will create two pending tasks and bill two credits. - **Don't post under-72h windows where you can't act.** A `pending` task you don't follow up on auto-cancels at 72h and refunds, but you also burn a manager's mindshare. - **Test on a single task first.** The single-shot path returns precise typed errors, easier to debug than a partial batch. - **CSV import?** The dashboard's CSV importer hits the same engine — every rule above applies there too. --- ## List & Read Comment Tasks Source: api/10-comments/62-list-and-read.md URL: https://developers.tokportal.com/list-and-read-comments # List & Read ## List ``` GET /api/ext/comments ``` Returns a paginated list of your tasks, newest first. | Param | Type | Description | | ------------------ | -------------- | -------------------------------------------------------------------------------------------- | | `status` | string | Comma-separated list of statuses to filter by — e.g. `status=pending,submitted`. | | `saved_account_id` | string (UUID) | Restrict to a specific account. | | `page` | integer | 1-indexed page (default 1). | | `per_page` | integer | 1–100 (default 25). | ```bash curl -X GET "https://app.tokportal.com/api/ext/comments?status=manually_confirmed&per_page=50" \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": [ { "id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d", "status": "manually_confirmed", "platform": "tiktok", "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://www.tiktok.com/@someone/video/7000000000000000000", "target_author_handle": "someone", "comment_text": "Brilliant idea", "cm_payout_amount": 0.24, "submitted_at": "2026-04-27T17:21:34Z", "verified_at": null, "manually_confirmed_at": "2026-04-28T13:56:11Z", "client_dispute_deadline_at": "2026-05-01T13:56:11Z", "finalized_at": null, "correction_required": null, "deadline_at": "2026-04-30T17:21:00Z", "created_at": "2026-04-27T17:18:37Z", "updated_at": "2026-04-28T13:56:11Z" } ], "pagination": { "page": 1, "per_page": 50, "total": 1, "total_pages": 1 } } ``` ## Read one ``` GET /api/ext/comments/{id} ``` Same task shape as in the list, wrapped under `data`. ```bash curl -X GET https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d \ -H "X-API-Key: sk_xxx" ``` Returns `404 COMMENT_TASK_NOT_FOUND` if the ID is unknown, `403 COMMENT_TASK_NOT_OWNED` if it belongs to another user. ## Cancel a pending task ``` DELETE /api/ext/comments/{id} ``` Refunds 1 credit and flips the task to `cancelled`. Only works while the task is still `pending` (the manager hasn't posted yet). ```bash curl -X DELETE https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d", "status": "cancelled", "credits_refunded": 1 } } ``` If the manager has already moved the task past `pending`, you'll get a `409 COMMENT_INVALID_STATUS` with the current status in `details.current_status`. From `submitted` onwards there is no cancellation — the workflow is already in flight. ## Field reference | Field | Type | Description | | ---------------------------- | ------------------- | ------------------------------------------------------------------------------------------ | | `id` | string (UUID) | Task ID. | | `status` | string | See the [lifecycle](./60-overview.md#lifecycle) for the full list. | | `platform` | string | `tiktok` / `instagram`. Snapshot of the account's platform at create time. | | `saved_account_id` | string (UUID) | The account posting the comment. | | `target_video_url` | string | Canonicalized URL of the target video (`tiktok.com/@x/video/...`, `instagram.com/reel/...`).| | `target_author_handle` | string \| null | Parsed from the URL when available (TikTok `/@handle/video/...`). | | `comment_text` | string | Exact text the manager posts. | | `cm_payout_amount` | number | Manager's USD payout. `0.15` while pending; final value once `verified_auto` / `finalized`.| | `submitted_at` | string \| null | When the manager declared they posted. | | `verified_at` | string \| null | When auto-verification matched the comment. | | `manually_confirmed_at` | string \| null | When the manager manually confirmed (couldn't auto-verify). | | `client_dispute_deadline_at` | string \| null | UTC cutoff for you to dispute a `manually_confirmed` task. Auto-finalizes after that. | | `finalized_at` | string \| null | When the payout was locked. | | `correction_required` | string \| null | Human-readable instructions when the task is `pending_corrections` (your dispute reason or a verifier-detected mismatch). | | `deadline_at` | string | Hard 72h deadline. Past this, `pending` tasks auto-cancel + refund. | | `created_at` / `updated_at` | string | ISO 8601 timestamps. | --- ## Approve & Dispute – Review Flow Source: api/10-comments/63-review-flow.md URL: https://developers.tokportal.com/comments-review-flow # 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 ``` ```bash curl -X POST https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d/approve \ -H "X-API-Key: sk_xxx" ``` **Response (200):** ```json { "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 in `manually_confirmed`. `details.current_status` tells 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. | ```bash curl -X POST https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d/dispute \ -H "X-API-Key: sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "reason": "Cannot find the comment under the video, please re-post." }' ``` **Response (200):** ```json { "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` — `reason` missing or too short. - `409 COMMENT_INVALID_STATUS` — task isn't in `manually_confirmed`. - All the standard 401/403/404s. ## A complete review loop in pseudo-code ```python import requests, time KEY = "sk_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() ``` --- ## Verifier Timeline – Verbose Audit Source: api/10-comments/64-verifications.md URL: https://developers.tokportal.com/comments-verifications # Verifier Timeline ``` GET /api/ext/comments/{id}/verifications ``` Returns the full list of auto-verification attempts on a task — useful when: - a task is stuck in `submitted` and you want to know **why** the verifier couldn't match, - you want to audit the auto-verifier's hit rate per platform, - you're building automation that decides between approve/dispute based on the scrape evidence. ```bash curl -X GET https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d/verifications \ -H "X-API-Key: sk_xxx" ``` **Response:** ```json { "data": { "task_id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d", "status": "manually_confirmed", "attempts": [ { "attempt_n": 1, "scraped_at": "2026-04-27T17:22:10Z", "outcome_kind": "couldnt_verify", "verified": false, "match_score": 0, "found_comment_id": null, "reason": "no_candidate_found", "comments_scraped": 87, "platform": "tiktok" } ] } } ``` ## Field reference | Field | Type | Description | | ------------------ | -------------- | ------------------------------------------------------------------------------------------ | | `attempt_n` | integer | 1-indexed attempt number on this task. | | `scraped_at` | string | When the scrape ran (UTC). | | `outcome_kind` | string | One of: `verified`, `wrong_text`, `wrong_account`, `couldnt_verify`, `scrape_failed`. | | `verified` | boolean | True only when `outcome_kind = "verified"`. | | `match_score` | number | Sørensen-Dice similarity of the best-matching comment (0–1). `0` if nothing matched. | | `found_comment_id` | string \| null | Platform comment ID of the best match (if any). | | `reason` | string | Internal reason tag — `matched`, `no_candidate_found`, `wrong_text_same_handle`, `wrong_account`, `scrape_failed_*`. | | `comments_scraped` | integer \| null | How many comments the scraper returned. `0` is a strong signal the scraper couldn't reach the video. | | `platform` | string \| null | Snapshot of the platform at scrape time — useful for cross-platform audits. | ## Outcome decision matrix | `outcome_kind` | Resulting task state | | ----------------- | --------------------------------------------------------------- | | `verified` | `verified_auto` → `finalized` after the 24h review window. | | `wrong_text` | `pending_corrections` (manager edits/reposts). | | `wrong_account` | `pending_corrections` (manager deletes + reposts from right account). | | `couldnt_verify` | Task stays in `submitted` — manager sees the manual modal. | | `scrape_failed` | Task stays in `submitted`; cron re-attempts later. | ## Why was my task not verified? If `comments_scraped` is `0` across all attempts, the Apify scraper couldn't reach the video — usually a private account or a regional block. Approve the task or contact the manager. If `comments_scraped` is high (40–100) and `outcome_kind` is `couldnt_verify`, the comment really wasn't found among the recent ones. Either: - the manager didn't actually post (dispute), - the comment was hidden / deleted by the creator (approve, your reach is gone but the manager did the work), - the comment is older than the 100 most recent (rare; approve). If `outcome_kind` is `wrong_text`, the manager posted a typo'd version. The `correction_required` text on the task spells out what they need to fix. If `outcome_kind` is `wrong_account`, the manager used a different account than `saved_account_id`. The manager will be told to delete + repost. --- ## Webhooks Source: api/11-webhooks/70-webhooks.md URL: https://developers.tokportal.com/webhooks # Webhooks Webhooks let TokPortal notify your backend when supported bundle and item lifecycle events happen. TokPortal currently emits these public webhook events: - `webhook.test` - `bundle.created` - `bundle.published` - `account.configured` - `account.published` - `account.in_review` - `account.pending_corrections` - `account.finalized` - `video.configured` - `video.in_review` - `video.published` - `video.pending_corrections` - `video.finalized` ## Event catalog ``` GET /webhooks/events ``` Use the event catalog to discover every supported event type, its delivery availability, example payload, payload schema, signature scheme, and required headers before creating an endpoint. ```bash curl https://app.tokportal.com/api/ext/webhooks/events ``` The response includes `events`, `envelope`, `delivery`, and `signature`. Events marked `emitted` are actively delivered today. ## Signature model When a webhook is delivered, TokPortal sends: | Header | Description | | ---------------------- | -------------------------------------------------------------- | | `TokPortal-Event-Id` | Stable event ID for idempotency. | | `TokPortal-Event-Type` | Event type, for example `bundle.published`. | | `TokPortal-Signature` | HMAC SHA-256 signature in the format `t=,v1=`. | Create endpoints with HTTPS URLs. Store the returned `signing_secret` immediately; it is only returned on creation. ## Create an endpoint ``` POST /webhooks ``` ### Node ```ts const response = await fetch("https://app.tokportal.com/api/ext/webhooks", { method: "POST", headers: { "X-API-Key": process.env.TOKPORTAL_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://example.com/tokportal/webhook", events: ["bundle.created", "account.in_review", "video.finalized"], description: "Production ingestion", }), }); if (!response.ok) { throw new Error(await response.text()); } const endpoint = await response.json(); console.log(endpoint.data.signing_secret); ``` ### Python ```python import os import requests response = requests.post( "https://app.tokportal.com/api/ext/webhooks", headers={ "X-API-Key": os.environ["TOKPORTAL_API_KEY"], "Content-Type": "application/json", }, json={ "url": "https://example.com/tokportal/webhook", "events": ["bundle.created", "account.in_review", "video.finalized"], "description": "Production ingestion", }, timeout=30, ) response.raise_for_status() endpoint = response.json() print(endpoint["data"]["signing_secret"]) ``` ### curl ```bash curl -X POST https://app.tokportal.com/api/ext/webhooks \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/tokportal/webhook", "events": ["bundle.created", "account.in_review", "video.finalized"], "description": "Production ingestion" }' ``` ### Response ```json { "data": { "id": "0b88db42-1111-4222-9333-e681165e6f4a", "url": "https://example.com/tokportal/webhook", "description": "Production ingestion", "events": ["bundle.created", "account.in_review", "video.finalized"], "enabled": true, "created_at": "2026-05-25T18:00:00Z", "updated_at": "2026-05-25T18:00:00Z", "last_delivery_at": null, "last_delivery_status": null, "failure_count": 0, "signing_secret": "whsec_..." } } ``` ## List endpoints ``` GET /webhooks ``` ```bash curl "https://app.tokportal.com/api/ext/webhooks?event=bundle.published&enabled=true" \ -H "X-API-Key: sk_your_key_here" ``` ## Update an endpoint ``` PATCH /webhooks/{id} ``` ```bash curl -X PATCH https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "enabled": false }' ``` ## Delete an endpoint ``` DELETE /webhooks/{id} ``` ```bash curl -X DELETE https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a \ -H "X-API-Key: sk_your_key_here" ``` ## Send a test event ``` POST /webhooks/{id}/test ``` This sends a signed `webhook.test` event to the endpoint and stores the delivery result. ### Node ```ts const response = await fetch( "https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a/test", { method: "POST", headers: { "X-API-Key": process.env.TOKPORTAL_API_KEY!, }, }, ); if (!response.ok) { throw new Error(await response.text()); } const delivery = await response.json(); console.log(delivery.data.success, delivery.data.status_code); ``` ### Python ```python import os import requests response = requests.post( "https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a/test", headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, timeout=30, ) response.raise_for_status() delivery = response.json() print(delivery["data"]["success"], delivery["data"]["status_code"]) ``` ### curl ```bash curl -X POST https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a/test \ -H "X-API-Key: sk_your_key_here" ``` ## List delivery attempts ``` GET /webhooks/{id}/deliveries ``` ```bash curl "https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a/deliveries?success=false" \ -H "X-API-Key: sk_your_key_here" ``` Each delivery includes the event ID, event type, HTTP status code, success flag, duration, error message, payload, and creation timestamp. ## Retry a delivery ``` POST /webhooks/{id}/deliveries/{delivery_id}/retry ``` Retry a stored delivery when your endpoint was temporarily unavailable. TokPortal reuses the stored webhook payload, preserves the original event ID, signs the request again with a fresh `TokPortal-Signature`, and records the retry as a new delivery attempt. ```bash curl -X POST https://app.tokportal.com/api/ext/webhooks/0b88db42-1111-4222-9333-e681165e6f4a/deliveries/7a1f3e5d-2222-4333-9444-abc123abc123/retry \ -H "X-API-Key: sk_your_key_here" ``` Receivers should treat `TokPortal-Event-Id` as the idempotency key. A retry can have a different delivery row ID while preserving the same event ID. ## Verify signatures The public Node SDK includes `verifyWebhookSignature`. The manual HMAC examples below are framework-agnostic and production-safe. In both cases, pass the exact raw request body bytes/string received by your HTTP framework, before JSON parsing or re-serialization. ### Node ```ts import { createHmac, timingSafeEqual } from "node:crypto"; function verifyTokPortalSignature( rawBody: Buffer | string, signatureHeader: string | null | undefined, secret: string, toleranceSeconds = 300, ) { if (!signatureHeader) { return false; } const parts = Object.fromEntries( signatureHeader.split(",").map((part) => { const [key, ...value] = part.trim().split("="); return [key, value.join("=")]; }), ); const timestamp = parts.t; const expectedHex = parts.v1; if (!timestamp || !expectedHex) { return false; } const timestampSeconds = Number(timestamp); if ( !Number.isFinite(timestampSeconds) || Math.abs(Date.now() / 1000 - timestampSeconds) > toleranceSeconds ) { return false; } const body = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(rawBody, "utf8"); const signedPayload = Buffer.concat([Buffer.from(`${timestamp}.`, "utf8"), body]); const digest = createHmac("sha256", secret).update(signedPayload).digest(); const expected = Buffer.from(expectedHex, "hex"); return expected.length === digest.length && timingSafeEqual(expected, digest); } const valid = verifyTokPortalSignature( rawBody, request.headers["tokportal-signature"], process.env.TOKPORTAL_WEBHOOK_SECRET!, ); ``` ### Python ```python import hashlib import hmac import time def verify_tokportal_signature( raw_body: bytes, signature_header: str | None, secret: str, tolerance_seconds: int = 300, ) -> bool: if not signature_header: return False parts = dict( item.strip().split("=", 1) for item in signature_header.split(",") if "=" in item ) timestamp = parts.get("t") expected = parts.get("v1") if not timestamp or not expected: return False try: timestamp_seconds = int(timestamp) except ValueError: return False if abs(time.time() - timestamp_seconds) > tolerance_seconds: return False signed_payload = timestamp.encode() + b"." + raw_body digest = hmac.new( secret.encode(), signed_payload, hashlib.sha256, ).hexdigest() return hmac.compare_digest(digest, expected) valid = verify_tokportal_signature( raw_body, request.headers["TokPortal-Signature"], os.environ["TOKPORTAL_WEBHOOK_SECRET"], ) ``` ### Go ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "io" "net/http" "os" "strconv" "strings" "time" ) func verifyTokPortalSignature(rawBody []byte, signatureHeader string, secret string, tolerance time.Duration) bool { parts := map[string]string{} for _, part := range strings.Split(signatureHeader, ",") { keyValue := strings.SplitN(strings.TrimSpace(part), "=", 2) if len(keyValue) == 2 { parts[keyValue[0]] = keyValue[1] } } timestamp := parts["t"] expectedHex := parts["v1"] if timestamp == "" || expectedHex == "" { return false } timestampSeconds, err := strconv.ParseInt(timestamp, 10, 64) if err != nil { return false } signedAt := time.Unix(timestampSeconds, 0) if time.Since(signedAt) > tolerance || time.Until(signedAt) > tolerance { return false } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(timestamp + ".")) mac.Write(rawBody) expected, err := hex.DecodeString(expectedHex) if err != nil { return false } return hmac.Equal(mac.Sum(nil), expected) } func handler(w http.ResponseWriter, r *http.Request) { rawBody, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "invalid body", http.StatusBadRequest) return } valid := verifyTokPortalSignature( rawBody, r.Header.Get("TokPortal-Signature"), os.Getenv("TOKPORTAL_WEBHOOK_SECRET"), 5*time.Minute, ) if !valid { http.Error(w, "invalid signature", http.StatusUnauthorized) return } } ``` The signature is computed over: ```txt . ``` using the endpoint `signing_secret`. Compare against the `v1` value in `TokPortal-Signature` with a constant-time comparison. ## Event payload ```json { "id": "evt_...", "type": "webhook.test", "api_version": "2026-05-25", "created_at": "2026-05-25T18:00:00Z", "data": { "webhook_endpoint_id": "0b88db42-1111-4222-9333-e681165e6f4a", "message": "TokPortal webhook test event" } } ``` --- ## API Reference Source: api/12-openapi-reference/90-openapi-reference.md URL: https://developers.tokportal.com/api-reference # API Reference This page is generated from the public OpenAPI schema. Do not edit it by hand. Generated operations: **78**. - [Download OpenAPI JSON](/openapi.json) - [Download OpenAPI YAML](/openapi.yaml) ## Profile ### Get authenticated user ```http GET /me ``` Operation ID: `getCurrentUser` | Status | Description | |---:|---| | `200` | Authenticated user profile and credit balance. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/me" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/me', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/me', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/me", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ## Reference ### List available countries ```http GET /countries ``` Operation ID: `listCountries` | Status | Description | |---:|---| | `200` | Enabled country codes available for account creation. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/countries" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/countries', { method: 'GET', headers: { } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/countries', headers={} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/countries", nil) resp, err := http.DefaultClient.Do(req) ``` ### List available platforms ```http GET /platforms ``` Operation ID: `listPlatforms` | Status | Description | |---:|---| | `200` | Supported social platforms. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/platforms" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/platforms', { method: 'GET', headers: { } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/platforms', headers={} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/platforms", nil) resp, err := http.DefaultClient.Do(req) ``` ## Credits ### Get credit pricing ```http GET /credit-costs ``` Operation ID: `getCreditCosts` | Status | Description | |---:|---| | `200` | Current credit cost table. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/credit-costs" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/credit-costs', { method: 'GET', headers: { } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/credit-costs', headers={} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/credit-costs", nil) resp, err := http.DefaultClient.Do(req) ``` ### Get credit balance ```http GET /credits/balance ``` Operation ID: `getCreditBalance` | Status | Description | |---:|---| | `200` | Current credit balance. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/credits/balance" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/credits/balance', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/credits/balance', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/credits/balance", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List credit transactions ```http GET /credits/history ``` Operation ID: `listCreditTransactions` | Parameter | In | Required | Description | |---|---:|---:|---| | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | Status | Description | |---:|---| | `200` | Paginated credit transaction history. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ## Bundles ### List bundles ```http GET /bundles ``` Operation ID: `listBundles` | Parameter | In | Required | Description | |---|---:|---:|---| | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | `status` | query | No | Filter by bundle status. | | `bundle_type` | query | No | | | `platform` | query | No | | | `external_ref` | query | No | | | `account_status` | query | No | | | Status | Description | |---:|---| | `200` | Paginated bundle list. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Create a bundle ```http POST /bundles ``` Operation ID: `createBundle` Creates an account-only, account-and-videos, or videos-only bundle. Credit cost is calculated server-side. Request body: `application/json` (CreateBundleRequest) | Status | Description | |---:|---| | `201` | Bundle created. | | `400` | Invalid request body or business rule violation. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | | `409` | Duplicate or invalid state. | | `429` | Create rate limit exceeded. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_only" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "bundle_type": "account_only" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "bundle_type": "account_only" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles", strings.NewReader("{\n \"bundle_type\": \"account_only\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Create bundles in bulk ```http POST /bundles/bulk ``` Operation ID: `createBundlesBulk` Request body: `application/json` (CreateBulkBundlesRequest) | Status | Description | |---:|---| | `201` | Bulk bundle creation result. | | `400` | Invalid request body or business rule violation. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | | `429` | Create rate limit exceeded. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/bulk" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": [ "tiktok" ], "country": "USA", "accounts_count": 25 }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/bulk', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "platforms": [ "tiktok" ], "country": "USA", "accounts_count": 25 }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/bulk', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "platforms": [ "tiktok" ], "country": "USA", "accounts_count": 25 } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/bulk", strings.NewReader("{\n \"platforms\": [\n \"tiktok\"\n ],\n \"country\": \"USA\",\n \"accounts_count\": 25\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Get a bundle ```http GET /bundles/{id} ``` Operation ID: `getBundle` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Bundle details. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Update bundle settings ```http PATCH /bundles/{id} ``` Operation ID: `updateBundle` Updates mutable bundle metadata such as title, external_ref, and auto_finalize_videos. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (PatchBundleRequest) | Status | Description | |---:|---| | `200` | Bundle updated. | | `400` | Invalid patch body. | | `401` | Missing, invalid, or revoked API key. | | `403` | Bundle belongs to another account. | | `404` | Bundle not found. | | `409` | Duplicate external_ref. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PATCH "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "auto_finalize_videos": false, "external_ref": "external_ref", "title": "Launch campaign" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'PATCH', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "auto_finalize_videos": false, "external_ref": "external_ref", "title": "Launch campaign" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PATCH', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "auto_finalize_videos": false, "external_ref": "external_ref", "title": "Launch campaign" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", strings.NewReader("{\n \"auto_finalize_videos\": false,\n \"external_ref\": \"external_ref\",\n \"title\": \"Launch campaign\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Publish a bundle ```http POST /bundles/{id}/publish ``` Operation ID: `publishBundle` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Publish result. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | | `404` | Bundle not found. | | `409` | Bundle is not ready or is in an invalid state. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Check bundle publish readiness ```http GET /bundles/{id}/publish-readiness ``` Operation ID: `getBundlePublishReadiness` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Readiness blockers and ready flag. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Unpublish a bundle ```http POST /bundles/{id}/unpublish ``` Operation ID: `unpublishBundle` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Unpublish result. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | | `409` | Bundle is in an invalid state. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Add video slots to a bundle ```http POST /bundles/{id}/add-video-slots ``` Operation ID: `addVideoSlots` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (QuantityRequest) | Status | Description | |---:|---| | `200` | Created video slots. | | `400` | Invalid quantity or bundle type. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "quantity": 1 }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "quantity": 1 }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "quantity": 1 } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots", strings.NewReader("{\n \"quantity\": 1\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Add edit slots to a bundle ```http POST /bundles/{id}/add-edit-slots ``` Operation ID: `addEditSlots` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (QuantityRequest) | Status | Description | |---:|---| | `200` | Created edit slots. | | `400` | Invalid quantity. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "quantity": 1 }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "quantity": 1 }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "quantity": 1 } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots", strings.NewReader("{\n \"quantity\": 1\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ## Account Configuration ### Get bundle account configuration ```http GET /bundles/{id}/account ``` Operation ID: `getBundleAccount` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Account configuration. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Configure bundle account profile ```http PUT /bundles/{id}/account ``` Operation ID: `configureBundleAccount` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (ConfigureAccountRequest) | Status | Description | |---:|---| | `200` | Configured account. | | `400` | Invalid account profile. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or account not found. | | `409` | Account status does not allow configuration. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PUT "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "username": "username", "visible_name": "visible_name" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', { method: 'PUT', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "username": "username", "visible_name": "visible_name" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PUT', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "username": "username", "visible_name": "visible_name" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account", strings.NewReader("{\n \"username\": \"username\",\n \"visible_name\": \"visible_name\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Request account corrections ```http POST /bundles/{id}/account/corrections ``` Operation ID: `requestBundleAccountCorrections` Moves an in-review account back to pending corrections with reviewer feedback. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (AccountCorrectionsRequest) | Status | Description | |---:|---| | `200` | Account corrections requested. | | `400` | Invalid correction body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or account not found. | | `409` | Account is not in review. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "comment": "Please review this item." }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "comment": "Please review this item." }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "comment": "Please review this item." } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections", strings.NewReader("{\n \"comment\": \"Please review this item.\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Finalize account review ```http POST /bundles/{id}/account/finalize ``` Operation ID: `finalizeBundleAccount` Approves an in-review account and marks it finalized. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Account finalized. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or account not found. | | `409` | Account is not in review. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ## Videos ### List bundle video slots ```http GET /bundles/{id}/videos ``` Operation ID: `listBundleVideos` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Video slots for the bundle. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get video slot configuration ```http GET /bundles/{id}/videos/{position} ``` Operation ID: `getBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | | Status | Description | |---:|---| | `200` | Video slot configuration. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Configure a video slot ```http PUT /bundles/{id}/videos/{position} ``` Operation ID: `configureBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | Request body: `application/json` (ConfigureVideoRequest) | Status | Description | |---:|---| | `200` | Configured video slot. | | `400` | Invalid video metadata. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video status does not allow configuration. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PUT "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', { method: 'PUT', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PUT', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1", strings.NewReader("{\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Patch video references ```http PATCH /bundles/{id}/videos/{position} ``` Operation ID: `patchBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | Request body: `application/json` (PatchVideoRequest) | Status | Description | |---:|---| | `200` | Patched video slot. | | `400` | Invalid patch body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PATCH "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "external_ref": "external_ref", "name": "name" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', { method: 'PATCH', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "external_ref": "external_ref", "name": "name" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PATCH', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "external_ref": "external_ref", "name": "name" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1", strings.NewReader("{\n \"external_ref\": \"external_ref\",\n \"name\": \"name\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Configure video slots in bulk ```http PUT /bundles/{id}/videos/batch ``` Operation ID: `batchConfigureBundleVideos` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `application/json` (BatchConfigureVideosRequest) | Status | Description | |---:|---| | `200` | Batch video configuration result. | | `400` | Invalid batch body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PUT "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date", "position": 1 } ] }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch', { method: 'PUT', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "videos": [ { "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date", "position": 1 } ] }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PUT', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "videos": [ { "video_type": "video", "description": "Campaign content description.", "target_publish_date": "target_publish_date", "position": 1 } ] } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch", strings.NewReader("{\n \"videos\": [\n {\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\",\n \"position\": 1\n }\n ]\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Publish all configured videos on an active bundle ```http POST /bundles/{id}/videos/publish-all ``` Operation ID: `publishAllBundleVideos` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | Status | Description | |---:|---| | `200` | Published video slots. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | | `409` | Bundle status does not allow publishing videos. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Import video slots from CSV ```http POST /bundles/{id}/videos/import-csv ``` Operation ID: `importBundleVideosCsv` Uploads a CSV file, downloads referenced media, and configures available video slots. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | Request body: `multipart/form-data` (multipart form) | Status | Description | |---:|---| | `200` | CSV import result. | | `400` | Invalid CSV or upload body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv" \ -H "X-API-Key: sk_your_key_here" \ -F "file=@/path/to/file" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Publish one video slot ```http POST /bundles/{id}/videos/{position}/publish ``` Operation ID: `publishBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | | Status | Description | |---:|---| | `200` | Video slot published. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Bundle or video status does not allow publishing. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Reset one video slot ```http POST /bundles/{id}/videos/{position}/reset ``` Operation ID: `resetBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | | Status | Description | |---:|---| | `200` | Video slot reset. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video status does not allow reset. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Unschedule one video slot ```http POST /bundles/{id}/videos/{position}/unschedule ``` Operation ID: `unscheduleBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | | Status | Description | |---:|---| | `200` | Video slot unscheduled. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video status does not allow unscheduling. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Finalize video review ```http POST /bundles/{id}/videos/{position}/finalize ``` Operation ID: `finalizeBundleVideo` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | | Status | Description | |---:|---| | `200` | Video slot finalized. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video is not in review. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Request video corrections ```http POST /bundles/{id}/videos/{position}/corrections ``` Operation ID: `requestBundleVideoCorrections` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | Request body: `application/json` (VideoCorrectionsRequest) | Status | Description | |---:|---| | `200` | Video corrections requested. | | `400` | Invalid correction body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video is not in review. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "comment": "Please review this item." }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "comment": "Please review this item." }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "comment": "Please review this item." } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections", strings.NewReader("{\n \"comment\": \"Please review this item.\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Fix a broken video download ```http POST /bundles/{id}/videos/{position}/fix-download ``` Operation ID: `fixBundleVideoDownload` Replaces a manager-flagged broken video or carousel download URL and clears the download issue flag. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Bundle ID. | | `position` | path | Yes | 1-based video slot position. | Request body: `application/json` (FixVideoDownloadRequest) | Status | Description | |---:|---| | `200` | Video download issue fixed. | | `400` | Invalid replacement media body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle or video slot not found. | | `409` | Video does not have a download issue. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '"value"' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify("value") }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json="value" ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download", strings.NewReader("\"value\"")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ## Accounts ### List delivered accounts ```http GET /accounts ``` Operation ID: `listAccounts` | Parameter | In | Required | Description | |---|---:|---:|---| | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | Status | Description | |---:|---| | `200` | Paginated delivered account list. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get a delivered account ```http GET /accounts/{id} ``` Operation ID: `getAccount` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Delivered account details. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List bundles for a delivered account ```http GET /accounts/{id}/bundles ``` Operation ID: `listAccountBundles` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | `status` | query | No | Filter by bundle status. | | Status | Description | |---:|---| | `200` | Paginated bundles for account. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Retrieve latest account verification code ```http POST /accounts/{id}/verification-code ``` Operation ID: `retrieveAccountVerificationCode` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Verification code result. | | `401` | Missing, invalid, or revoked API key. | | `402` | Owning fee required. | | `404` | Account or verification code not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Reveal delivered account credentials ```http POST /accounts/{id}/reveal-credentials ``` Operation ID: `revealAccountCredentials` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Credentials reveal result. | | `401` | Missing, invalid, or revoked API key. | | `402` | Owning fee required. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get active account edit request ```http GET /accounts/{id}/edit-request ``` Operation ID: `getAccountEditRequest` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Active edit request. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Request profile edits for a delivered account ```http POST /accounts/{id}/edit-request ``` Operation ID: `createAccountEditRequest` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | Request body: `application/json` (AccountEditRequest) | Status | Description | |---:|---| | `201` | Edit request created. | | `400` | Invalid edit request. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | | `409` | An active edit request already exists. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "requested_username": "requested_username", "requested_visible_name": "requested_visible_name" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "requested_username": "requested_username", "requested_visible_name": "requested_visible_name" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "requested_username": "requested_username", "requested_visible_name": "requested_visible_name" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request", strings.NewReader("{\n \"requested_username\": \"requested_username\",\n \"requested_visible_name\": \"requested_visible_name\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ## Analytics ### Check analytics refresh availability ```http GET /accounts/{id}/analytics/can-refresh ``` Operation ID: `canRefreshAccountAnalytics` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Manual refresh availability. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Refresh account analytics ```http POST /accounts/{id}/analytics/refresh ``` Operation ID: `refreshAccountAnalytics` Backward-compatible account analytics refresh path. Supports forced refresh and post import options. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | Request body: `application/json` (RefreshAnalyticsRequest) | Status | Description | |---:|---| | `200` | Refresh result. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | | `404` | Account not found. | | `429` | Manual refresh cooldown active. | | `502` | Refresh provider failed. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh", strings.NewReader("{\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Get analytics dashboard ```http GET /analytics ``` Operation ID: `getAnalyticsDashboard` | Status | Description | |---:|---| | `200` | Analytics dashboard data. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get analytics data contract ```http GET /analytics/contract ``` Operation ID: `getAnalyticsContract` Returns the Analytics v2 contract, current access payload, metric semantics, freshness targets, and redaction rules. | Status | Description | |---:|---| | `200` | Analytics contract and access payload. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/contract" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/contract', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/contract', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/contract", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Export analytics videos CSV ```http GET /analytics/export/videos ``` Operation ID: `exportAnalyticsVideos` | Parameter | In | Required | Description | |---|---:|---:|---| | `account` | query | No | Repeatable account filter. | | `workspace` | query | No | | | `platform` | query | No | Repeatable platform filter. | | `country` | query | No | Repeatable country filter. | | `q` | query | No | Search query. | | `from` | query | No | | | `to` | query | No | | | Status | Description | |---:|---| | `200` | CSV export of analytics video rows. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Create analytics web report ```http POST /analytics/export/reports ``` Operation ID: `createAnalyticsReport` Creates a shareable Analytics v2 web report and returns its token and URL. Request body: `application/json` (CreateAnalyticsReportRequest) | Status | Description | |---:|---| | `200` | Created analytics report. | | `400` | Invalid report request. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/analytics/export/reports" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/reports', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/analytics/export/reports', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/analytics/export/reports", strings.NewReader("{\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Export analytics report HTML ```http POST /analytics/export/reports/html ``` Operation ID: `exportAnalyticsReportHtml` Creates a standalone downloadable HTML analytics report. Request body: `application/json` (CreateAnalyticsReportRequest) | Status | Description | |---:|---| | `200` | Standalone analytics report HTML. | | `400` | Invalid report request. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/analytics/export/reports/html" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/reports/html', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/analytics/export/reports/html', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "title": "Launch campaign", "template": "template", "brandName": "brandName", "brandAccent": "brandAccent" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/analytics/export/reports/html", strings.NewReader("{\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Get analytics time series ```http GET /analytics/series ``` Operation ID: `getAnalyticsSeries` | Parameter | In | Required | Description | |---|---:|---:|---| | `metric` | query | No | | | `granularity` | query | No | | | `mode` | query | No | | | `account` | query | No | Repeatable account filter. | | `from` | query | No | | | `to` | query | No | | | Status | Description | |---:|---| | `200` | Analytics time series. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get account analytics drilldown ```http GET /analytics/accounts/{id} ``` Operation ID: `getAnalyticsAccount` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Account analytics drilldown. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Refresh analytics account ```http POST /analytics/accounts/{id}/refresh ``` Operation ID: `refreshAnalyticsAccount` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | Request body: `application/json` (RefreshAnalyticsRequest) | Status | Description | |---:|---| | `200` | Refresh result. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | | `404` | Account not found. | | `429` | Manual refresh cooldown active. | | `502` | Refresh provider failed. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "force": false, "includePosts": false, "includeComments": false, "forcePosts": false } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh", strings.NewReader("{\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### List raw account analytics snapshots ```http GET /analytics/accounts/{id}/raw ``` Operation ID: `listAnalyticsAccountRawSnapshots` Returns owner-scoped stored raw provider payloads for a saved account. Full analytics tier only. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | `source` | query | No | | | `limit` | query | No | | | `from` | query | No | | | `to` | query | No | | | Status | Description | |---:|---| | `200` | Raw account analytics snapshots. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include raw analytics payloads. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get account analytics compatibility view ```http GET /accounts/{id}/analytics ``` Operation ID: `getAccountAnalytics` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | Status | Description | |---:|---| | `200` | Account analytics data. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List post analytics for an account ```http GET /accounts/{id}/analytics/videos ``` Operation ID: `listAccountVideoAnalytics` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | `sort_by` | query | No | | | `sort_order` | query | No | | | Status | Description | |---:|---| | `200` | Post analytics list. | | `401` | Missing, invalid, or revoked API key. | | `404` | Account not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get single video analytics ```http GET /videos/{id}/analytics ``` Operation ID: `getVideoAnalytics` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Video ID. | | Status | Description | |---:|---| | `200` | Video analytics. | | `401` | Missing, invalid, or revoked API key. | | `404` | Video not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Get comment pulse analytics ```http GET /analytics/comments ``` Operation ID: `getCommentPulse` | Parameter | In | Required | Description | |---|---:|---:|---| | `platform` | query | No | | | `country` | query | No | | | `account` | query | No | | | `post` | query | No | | | `limit` | query | No | | | `postLimit` | query | No | | | `from` | query | No | | | `to` | query | No | | | Status | Description | |---:|---| | `200` | Comment pulse analytics. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List comments for an account post ```http GET /analytics/accounts/{id}/comments ``` Operation ID: `listAnalyticsAccountComments` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Saved account ID. | | `trackedPostId` | query | No | | | `postId` | query | No | | | `limit` | query | No | | | Status | Description | |---:|---| | `200` | Post comments. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include this analytics level. | | `404` | Account or post not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List raw post analytics snapshots ```http GET /analytics/posts/{id}/raw ``` Operation ID: `listAnalyticsPostRawSnapshots` Returns owner-scoped stored raw provider payloads for a tracked post. Full analytics tier only. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Tracked post ID. | | `source` | query | No | | | `limit` | query | No | | | `from` | query | No | | | `to` | query | No | | | Status | Description | |---:|---| | `200` | Raw post analytics snapshots. | | `401` | Missing, invalid, or revoked API key. | | `403` | Plan does not include raw analytics payloads. | | `404` | Post not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ## Webhooks ### List webhook event catalog ```http GET /webhooks/events ``` Operation ID: `listWebhookEvents` Returns the supported webhook event types, delivery envelope, signature scheme, and example payloads. This endpoint is public so teams can inspect webhook contracts before creating an API key. | Status | Description | |---:|---| | `200` | Supported webhook events and delivery contract. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/webhooks/events" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/events', { method: 'GET', headers: { } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/webhooks/events', headers={} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/webhooks/events", nil) resp, err := http.DefaultClient.Do(req) ``` ### List webhook endpoints ```http GET /webhooks ``` Operation ID: `listWebhookEndpoints` | Parameter | In | Required | Description | |---|---:|---:|---| | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | `enabled` | query | No | Filter by enabled state. | | `event` | query | No | Filter endpoints subscribed to an event. | | Status | Description | |---:|---| | `200` | Paginated webhook endpoint list. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Create a webhook endpoint ```http POST /webhooks ``` Operation ID: `createWebhookEndpoint` Creates a webhook endpoint and returns its signing secret once. Store the secret to verify TokPortal webhook signatures. Request body: `application/json` (CreateWebhookEndpointRequest) | Status | Description | |---:|---| | `201` | Webhook endpoint created. | | `400` | Invalid webhook endpoint. | | `401` | Missing, invalid, or revoked API key. | | `409` | Endpoint already exists. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/webhooks" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/resource", "events": [ "webhook.test" ] }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "url": "https://example.com/resource", "events": [ "webhook.test" ] }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/webhooks', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "url": "https://example.com/resource", "events": [ "webhook.test" ] } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/webhooks", strings.NewReader("{\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ]\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Get a webhook endpoint ```http GET /webhooks/{id} ``` Operation ID: `getWebhookEndpoint` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | | Status | Description | |---:|---| | `200` | Webhook endpoint details. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Update a webhook endpoint ```http PATCH /webhooks/{id} ``` Operation ID: `updateWebhookEndpoint` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | Request body: `application/json` (UpdateWebhookEndpointRequest) | Status | Description | |---:|---| | `200` | Webhook endpoint updated. | | `400` | Invalid webhook endpoint patch. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X PATCH "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/resource", "events": [ "webhook.test" ], "description": "Campaign content description.", "enabled": false }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'PATCH', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "url": "https://example.com/resource", "events": [ "webhook.test" ], "description": "Campaign content description.", "enabled": false }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'PATCH', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "url": "https://example.com/resource", "events": [ "webhook.test" ], "description": "Campaign content description.", "enabled": false } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", strings.NewReader("{\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ],\n \"description\": \"Campaign content description.\",\n \"enabled\": false\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Delete a webhook endpoint ```http DELETE /webhooks/{id} ``` Operation ID: `deleteWebhookEndpoint` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | | Status | Description | |---:|---| | `204` | Webhook endpoint deleted. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X DELETE "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'DELETE', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'DELETE', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("DELETE", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### List webhook deliveries ```http GET /webhooks/{id}/deliveries ``` Operation ID: `listWebhookDeliveries` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | `event_type` | query | No | Filter by event type. | | `success` | query | No | Filter by delivery success. | | Status | Description | |---:|---| | `200` | Paginated delivery attempts for the webhook endpoint. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Retry a webhook delivery ```http POST /webhooks/{id}/deliveries/{delivery_id}/retry ``` Operation ID: `retryWebhookDelivery` Resends the stored webhook payload to the endpoint's current URL with a fresh TokPortal-Signature header. The event ID is preserved so receivers can keep idempotent processing. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | | `delivery_id` | path | Yes | Webhook delivery ID. | | Status | Description | |---:|---| | `200` | Retry delivery result. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint or delivery not found. | | `409` | Webhook endpoint is disabled or payload is unavailable. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Send a test webhook ```http POST /webhooks/{id}/test ``` Operation ID: `testWebhookEndpoint` Sends a signed webhook.test event to the endpoint and records the delivery result. | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Webhook endpoint ID. | | Status | Description | |---:|---| | `200` | Test delivery result. | | `401` | Missing, invalid, or revoked API key. | | `404` | Webhook endpoint not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ## Uploads ### Create a video upload URL ```http POST /upload/video ``` Operation ID: `uploadVideo` Request body: `application/json` (UploadVideoRequest) | Status | Description | |---:|---| | `200` | Presigned video upload URL. | | `400` | Invalid upload request. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/upload/video" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "filename": "media.mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/upload/video', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "filename": "media.mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/upload/video', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "filename": "media.mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/video", strings.NewReader("{\n \"filename\": \"media.mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Upload a video file directly ```http POST /upload/video/direct ``` Operation ID: `uploadVideoDirect` Uploads multipart/form-data directly through TokPortal and returns the public video URL. Request body: `multipart/form-data` (multipart form) | Status | Description | |---:|---| | `200` | Uploaded video URL. | | `400` | Invalid upload. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/upload/video/direct" \ -H "X-API-Key: sk_your_key_here" \ -F "file=@/path/to/file" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/upload/video/direct', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/upload/video/direct', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/video/direct", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Create an image upload URL ```http POST /upload/image ``` Operation ID: `uploadImage` Request body: `application/json` (UploadImageRequest) | Status | Description | |---:|---| | `200` | Signed image upload URL. | | `400` | Invalid upload request. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/upload/image" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "filename": "media.mp4", "content_type": "video/mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/upload/image', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "filename": "media.mp4", "content_type": "video/mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/upload/image', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "filename": "media.mp4", "content_type": "video/mp4", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/image", strings.NewReader("{\n \"filename\": \"media.mp4\",\n \"content_type\": \"video/mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Upload an image file directly ```http POST /upload/image/direct ``` Operation ID: `uploadImageDirect` Uploads multipart/form-data directly through TokPortal and returns storage details. HEIF/HEIC may be converted to JPEG. Request body: `multipart/form-data` (multipart form) | Status | Description | |---:|---| | `200` | Uploaded image URL. | | `400` | Invalid upload. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/upload/image/direct" \ -H "X-API-Key: sk_your_key_here" \ -F "file=@/path/to/file" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/upload/image/direct', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/upload/image/direct', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/image/direct", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Import an image from URL ```http POST /upload/image/from-url ``` Operation ID: `uploadImageFromUrl` Fetches a public direct image URL and stores it permanently in TokPortal storage. Request body: `application/json` (UploadImageFromUrlRequest) | Status | Description | |---:|---| | `200` | Stored image details. | | `400` | Invalid image URL or request body. | | `401` | Missing, invalid, or revoked API key. | | `404` | Bundle not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/upload/image/from-url" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/resource", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/upload/image/from-url', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "url": "https://example.com/resource", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/upload/image/from-url', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "url": "https://example.com/resource", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/image/from-url", strings.NewReader("{\n \"url\": \"https://example.com/resource\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ## Comments ### List comment tasks ```http GET /comments ``` Operation ID: `listCommentTasks` | Parameter | In | Required | Description | |---|---:|---:|---| | `page` | query | No | Page number. | | `per_page` | query | No | Items per page. | | Status | Description | |---:|---| | `200` | Paginated comment task list. | | `401` | Missing, invalid, or revoked API key. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/comments?page=example&per_page=example" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments?page=example&per_page=example', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/comments?page=example&per_page=example', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/comments?page=example&per_page=example", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Create comment tasks ```http POST /comments ``` Operation ID: `createCommentTasks` Request body: `application/json` (CreateCommentTaskRequest) | Status | Description | |---:|---| | `201` | Created comment tasks. | | `400` | Invalid comment task request. | | `401` | Missing, invalid, or revoked API key. | | `402` | Insufficient credits. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/comments" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://example.com/resource", "comment_text": "comment_text" }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://example.com/resource", "comment_text": "comment_text" }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/comments', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "target_video_url": "https://example.com/resource", "comment_text": "comment_text" } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/comments", strings.NewReader("{\n \"saved_account_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n \"target_video_url\": \"https://example.com/resource\",\n \"comment_text\": \"comment_text\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### Get a comment task ```http GET /comments/{id} ``` Operation ID: `getCommentTask` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Comment task ID. | | Status | Description | |---:|---| | `200` | Comment task. | | `401` | Missing, invalid, or revoked API key. | | `404` | Comment task not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Delete a comment task ```http DELETE /comments/{id} ``` Operation ID: `deleteCommentTask` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Comment task ID. | | Status | Description | |---:|---| | `200` | Delete result. | | `401` | Missing, invalid, or revoked API key. | | `404` | Comment task not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X DELETE "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', { method: 'DELETE', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'DELETE', 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("DELETE", "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Approve a manually confirmed comment task ```http POST /comments/{id}/approve ``` Operation ID: `approveCommentTask` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Comment task ID. | | Status | Description | |---:|---| | `200` | Approved comment task. | | `401` | Missing, invalid, or revoked API key. | | `404` | Comment task not found. | | `409` | Comment task status cannot be approved. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` ### Dispute a manually confirmed comment task ```http POST /comments/{id}/dispute ``` Operation ID: `disputeCommentTask` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Comment task ID. | Request body: `application/json` (DisputeCommentRequest) | Status | Description | |---:|---| | `200` | Disputed comment task. | | `400` | Invalid dispute reason. | | `401` | Missing, invalid, or revoked API key. | | `404` | Comment task not found. | | `409` | Comment task status cannot be disputed. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | | `Idempotent-Replayed` | Present with value true when a mutating request is replayed from a completed Idempotency-Key. | #### Examples **curl** ```bash curl -X POST "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute" \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "reason": "Please review this item." }' ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute', { method: 'POST', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ "reason": "Please review this item." }) }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'POST', 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}, json={ "reason": "Please review this item." } ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute", strings.NewReader("{\n \"reason\": \"Please review this item.\"\n}")) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) ``` ### List comment task verification events ```http GET /comments/{id}/verifications ``` Operation ID: `listCommentTaskVerifications` | Parameter | In | Required | Description | |---|---:|---:|---| | `id` | path | Yes | Comment task ID. | | Status | Description | |---:|---| | `200` | Verification timeline. | | `401` | Missing, invalid, or revoked API key. | | `404` | Comment task not found. | #### Response headers | Header | Description | |---|---| | `X-TokPortal-API-Version` | Current TokPortal public API contract version. | | `X-TokPortal-API-Stability` | Stability channel for the public API contract. | | `X-TokPortal-Request-ID` | Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier. | | `X-RateLimit-Limit` | Maximum token bucket capacity for the authenticated API key. | | `X-RateLimit-Remaining` | Approximate requests remaining for the authenticated API key after this request. | | `X-RateLimit-Reset` | Unix timestamp when the token bucket is expected to be full again. | | `Retry-After` | Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses. | #### Examples **curl** ```bash curl -X GET "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications" \ -H "X-API-Key: sk_your_key_here" ``` **Node** ```ts const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications', { method: 'GET', headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY!, } }); const data = await response.json(); ``` **Python** ```python import os import requests response = requests.request( 'GET', 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications', headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]} ) print(response.json()) ``` **Go** ```go req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications", nil) req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY")) resp, err := http.DefaultClient.Do(req) ``` --- ## TokPortal + n8n - TikTok Workflow via HTTP Source: use-cases/01-no-code/90-n8n-tiktok-automation.md URL: https://developers.tokportal.com/use-cases/n8n-tiktok-automation # TokPortal + n8n TokPortal does not currently provide a native n8n node. Use n8n's **HTTP Request** node with the public REST API. This workflow creates a TikTok bundle, configures the account profile, configures one or more video slots from public video URLs, then publishes the bundle. TokPortal handles the order lifecycle after publish; the API does not guarantee views, reach, or avoidance of platform enforcement. ## Prerequisites - A [TokPortal account](https://app.tokportal.com/auth/signup) with credits - A TokPortal API key from the [Developer Portal](https://app.tokportal.com/developer/api-keys) - n8n Cloud or self-hosted n8n - Video files available as public/direct URLs, or uploaded through [Media Upload](/media-upload) ## 1. Create the HTTP credential Create an n8n credential for header authentication: | Field | Value | |-------|-------| | Header Name | `X-API-Key` | | Header Value | `sk_your_key_here` | Every TokPortal API request also needs `Content-Type: application/json` when the body is JSON. ## 2. Trigger the workflow Typical triggers: | Trigger | Expected fields | |---------|-----------------| | Google Sheets row | `country`, `username`, `visible_name`, `bio`, `profile_picture_url`, `video_url`, `description`, `target_publish_date` | | Webhook | JSON payload from your CMS or internal tool | | Schedule | Pull queued rows from Airtable, Sheets, or a database | Use country codes returned by `GET /countries`. `US` and `GB` aliases are accepted by the API, but the platform may return canonical codes such as `USA` or `UK`. ## 3. Create a bundle Add an **HTTP Request** node: ```json { "method": "POST", "url": "https://app.tokportal.com/api/ext/bundles", "headers": { "Content-Type": "application/json" }, "body": { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "title": "n8n TikTok workflow", "videos_quantity": 3, "external_ref": "n8n-{{$json.row_id}}" } } ``` Save `{{$json.data.bundle_id}}` from the response. Costs are calculated server-side and returned as `credits_charged` and `cost_breakdown`. Reference: [Create Bundle](/create-bundle) ## 4. Configure the account profile Account configuration is required before publishing. ```json { "method": "PUT", "url": "https://app.tokportal.com/api/ext/bundles/{{$json.data.bundle_id}}/account", "headers": { "Content-Type": "application/json" }, "body": { "username": "mybrand_us", "visible_name": "My Brand", "biography": "Daily product videos.", "profile_picture_url": "https://cdn.example.com/mybrand/profile.jpg" } } ``` Reference: [Account Configuration](/account-configuration) ## 5. Configure videos If your trigger already provides a public video URL, pass it directly as `video_url`. ```json { "method": "PUT", "url": "https://app.tokportal.com/api/ext/bundles/{{bundle_id}}/videos/1", "headers": { "Content-Type": "application/json" }, "body": { "video_type": "video", "description": "New drop is live #newarrival", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/videos/drop-01.mp4", "external_ref": "drop-01" } } ``` For local files or browser uploads, first use [Media Upload](/media-upload): - `POST /upload/video/direct` uploads the file through TokPortal. - `POST /upload/video` returns a presigned URL so a client can upload directly to storage. - For 50-100 MB videos, prefer presigned upload or a stable public URL instead of routing the binary through n8n/Zapier if your automation plan has small payload limits. Reference: [Configure Videos](/configure-videos) ## 6. Publish When the account and at least one video are configured, publish the bundle: ```json { "method": "POST", "url": "https://app.tokportal.com/api/ext/bundles/{{bundle_id}}/publish" } ``` If publish is blocked, call `GET /bundles/{id}/publish-readiness` or read the publish error response to see which account/video fields are missing. Reference: [Publish & Unpublish](/publish-unpublish) ## Multi-country workflows `POST /bundles/bulk` creates multiple bundles for one `country` and one or more `platforms`. To cover several countries in n8n, loop over a country list and call the bulk endpoint once per country. ```json { "method": "POST", "url": "https://app.tokportal.com/api/ext/bundles/bulk", "headers": { "Content-Type": "application/json" }, "body": { "platforms": ["tiktok"], "country": "US", "accounts_count": 5, "upload_accounts_count": 2, "videos_per_account": 10, "wants_niche_warming": true, "niche_warming_instructions": "Lifestyle and product review content.", "external_ref": "n8n-q2-us" } } ``` This creates 5 TikTok bundles in the selected country. The first 2 bundles include video slots; the remaining 3 are account-only bundles. Reference: [Create Bulk](/create-bulk) ## Monitoring Add optional n8n branches for: - `GET /bundles/{id}` to check bundle status - [Webhooks](/webhooks) to receive `bundle.*`, `account.*`, and `video.*` events - [Analytics](/analytics) after accounts and posts are live Related guides: [Zapier](/use-cases/no-code/zapier-social-media-automation), [Make](/use-cases/no-code/make-tiktok-automation), [Python Quickstart](/use-cases/platform-guides/python-quickstart). --- ## TokPortal + Zapier - Social Campaigns via Webhooks Source: use-cases/01-no-code/96-zapier-social-media-automation.md URL: https://developers.tokportal.com/use-cases/zapier-social-media-automation # TokPortal + Zapier TokPortal does not currently provide a native Zapier app. Use **Webhooks by Zapier** with the public REST API. The API can create bundles, configure account profiles, configure videos from public URLs, publish bundles, and read status/analytics. Zapier handles the trigger and data mapping; TokPortal handles the order lifecycle after publish. ## Authentication Add these headers to every Webhooks action: | Header | Value | |--------|-------| | `X-API-Key` | `sk_your_key_here` | | `Content-Type` | `application/json` | Do not use `Authorization: Bearer` for TokPortal API keys. ## Example Zap: Google Sheets to TikTok bundle Sheet columns: ```text country,username,visible_name,biography,profile_picture_url,video_url,description,target_publish_date ``` ### 1. Trigger Use **Google Sheets > New Spreadsheet Row** or any equivalent app trigger. ### 2. Create bundle Action: **Webhooks by Zapier > Custom Request** ```text Method: POST URL: https://app.tokportal.com/api/ext/bundles Headers: X-API-Key: sk_your_key_here Content-Type: application/json Body: { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "{{country}}", "videos_quantity": 1, "external_ref": "zapier-{{zap_meta_human_now}}" } ``` Zapier should map `data.bundle_id` from the response into later actions. Reference: [Create Bundle](/create-bundle) ### 3. Configure account ```text Method: PUT URL: https://app.tokportal.com/api/ext/bundles/{{data.bundle_id}}/account Headers: X-API-Key: sk_your_key_here Content-Type: application/json Body: { "username": "{{username}}", "visible_name": "{{visible_name}}", "biography": "{{biography}}", "profile_picture_url": "{{profile_picture_url}}" } ``` Reference: [Account Configuration](/account-configuration) ### 4. Configure video ```text Method: PUT URL: https://app.tokportal.com/api/ext/bundles/{{data.bundle_id}}/videos/1 Headers: X-API-Key: sk_your_key_here Content-Type: application/json Body: { "video_type": "video", "description": "{{description}}", "target_publish_date": "{{target_publish_date}}", "video_url": "{{video_url}}" } ``` `video_url` can be a public/direct URL or a `public_url` returned by [Media Upload](/media-upload). If your Zap receives a large binary file, use a public URL or presigned upload flow instead of sending the file through Zapier. Reference: [Configure Videos](/configure-videos) ### 5. Publish ```text Method: POST URL: https://app.tokportal.com/api/ext/bundles/{{data.bundle_id}}/publish Headers: X-API-Key: sk_your_key_here ``` Reference: [Publish & Unpublish](/publish-unpublish) ## Useful Zap patterns | Trigger App | Pattern | |-------------|---------| | Airtable | New approved content brief creates a bundle | | Dropbox | New shared video URL configures a video slot | | Shopify | New product launch creates a content workflow | | Slack | Slash command triggers a prefilled bundle | | Webhooks | Your CMS posts campaign payloads into Zapier | ## Monitoring For reliable production workflows, add one of these: - Poll `GET /bundles/{id}` until the status changes. - Use [TokPortal webhooks](/webhooks) to send lifecycle events to a Zapier Catch Hook. - Pull [Analytics](/analytics) only after the delivered account has live posts. Related guides: [n8n](/use-cases/no-code/n8n-tiktok-automation), [Make](/use-cases/no-code/make-tiktok-automation), [Python Quickstart](/use-cases/platform-guides/python-quickstart). --- ## TokPortal + Make - TikTok Workflow via HTTP Source: use-cases/01-no-code/make-tiktok-automation.md URL: https://developers.tokportal.com/use-cases/make-tiktok-automation # TokPortal + Make TokPortal does not currently ship a native Make app. Use Make's **HTTP** module to call the REST API with `X-API-Key`. The realistic flow is: 1. Trigger from a row, webhook, schedule, or Airtable record. 2. Create a TokPortal bundle. 3. Configure the required account fields. 4. Configure one or more video slots. 5. Publish the bundle. 6. Optionally monitor with webhooks or status polling. ## Prerequisites - A [TokPortal account](https://app.tokportal.com/auth/signup) with credits - A TokPortal API key from the [Developer Portal](https://app.tokportal.com/developer/api-keys) - A Make account - Public media URLs or files uploaded through [Media Upload](/media-upload) ## 1. Create the scenario trigger Common trigger data: | Field | Example | |-------|---------| | `country` | `US`, `USA`, `UK`, `FR` | | `username` | `mybrand_us` | | `visible_name` | `My Brand` | | `biography` | `Daily product videos.` | | `profile_picture_url` | `https://cdn.example.com/profile.jpg` | | `video_url` | `https://cdn.example.com/video.mp4` | | `description` | `New drop is live #newarrival` | | `target_publish_date` | `2026-06-05` | Use `GET /countries` if you need to validate enabled countries before creating bundles. ## 2. Create bundle Add **HTTP > Make a request**: | Setting | Value | |---------|-------| | URL | `https://app.tokportal.com/api/ext/bundles` | | Method | `POST` | | Body type | Raw | | Content type | JSON | Headers: | Name | Value | |------|-------| | `X-API-Key` | `sk_your_key_here` | | `Content-Type` | `application/json` | Request body: ```json { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "{{1.country}}", "videos_quantity": 1, "title": "Make workflow {{1.row_id}}", "external_ref": "make-{{1.row_id}}" } ``` Store `data.bundle_id` from the response for the next modules. Do not use `Authorization: Bearer`; TokPortal API keys are sent in `X-API-Key`. Reference: [Create Bundle](/create-bundle) ## 3. Configure account Add another HTTP module: | Setting | Value | |---------|-------| | URL | `https://app.tokportal.com/api/ext/bundles/{{2.data.bundle_id}}/account` | | Method | `PUT` | | Body type | Raw JSON | ```json { "username": "{{1.username}}", "visible_name": "{{1.visible_name}}", "biography": "{{1.biography}}", "profile_picture_url": "{{1.profile_picture_url}}" } ``` Reference: [Account Configuration](/account-configuration) ## 4. Configure video If the source app provides a public file URL, pass it directly: ```json { "video_type": "video", "description": "{{1.description}}", "target_publish_date": "{{1.target_publish_date}}", "video_url": "{{1.video_url}}", "external_ref": "make-{{1.row_id}}-video-1" } ``` Module URL: ```text https://app.tokportal.com/api/ext/bundles/{{2.data.bundle_id}}/videos/1 ``` Method: `PUT` TokPortal can also ingest media uploaded through [Media Upload](/media-upload). If Make's file handling becomes the bottleneck for large files, upload by public URL or presigned URL and pass the returned `public_url` as `video_url`. Reference: [Configure Videos](/configure-videos) ## 5. Publish Add a final HTTP module: | Setting | Value | |---------|-------| | URL | `https://app.tokportal.com/api/ext/bundles/{{2.data.bundle_id}}/publish` | | Method | `POST` | No JSON body is required. Reference: [Publish & Unpublish](/publish-unpublish) ## Error handling Add Make error routes for common responses: | Status | Meaning | |--------|---------| | `400` | Missing/invalid fields; inspect `error.details` | | `401` | Missing, invalid, or revoked API key | | `402` | Not enough credits | | `429` | Rate limit; retry after the returned delay | For publish issues, call: ```text GET https://app.tokportal.com/api/ext/bundles/{{bundle_id}}/publish-readiness ``` ## Multi-country scenarios The bulk endpoint accepts one `country` per request. For multiple countries, use an Iterator in Make: ```json { "platforms": ["tiktok", "instagram"], "country": "{{iterator.country}}", "accounts_count": 3, "upload_accounts_count": 1, "videos_per_account": 5, "external_ref": "make-launch-{{iterator.country}}" } ``` This creates 3 bundles per platform in the current country; 1 bundle per platform receives video slots. Related docs: [Create Bulk](/create-bulk), [Webhooks](/webhooks), [Analytics](/analytics), [SDKs & CLI](/sdks-cli). --- ## TokPortal + OpenClaw Source: use-cases/02-ai-agents/91-openclaw-tiktok.md URL: https://developers.tokportal.com/use-cases/openclaw-tiktok-posting # TokPortal + OpenClaw TokPortal has an OpenClaw integration path through ClawHub. The important technical detail is that the skill exposes the same public TokPortal API surface as the [MCP server](/mcp): bundles, account configuration, videos, webhooks, uploads, accounts, and analytics. ## Install ```bash clawhub install tokportal ``` Skill page: [clawhub.ai/naybu256/tokportal](https://clawhub.ai/naybu256/tokportal) ## Configure Set a TokPortal API key from the [Developer Portal](https://app.tokportal.com/developer/api-keys). Depending on your OpenClaw setup, configure the key either in the skill config or as an environment variable: ```bash TOKPORTAL_API_KEY=sk_your_key_here ``` ## What the agent can do The agent can only do what the public API supports. It can call generated tools such as: | Tool | API surface | |------|-------------| | `tokportal_get_credit_balance` | `GET /credits/balance` | | `tokportal_create_bundle` | `POST /bundles` | | `tokportal_create_bundles_bulk` | `POST /bundles/bulk` | | `tokportal_configure_bundle_account` | `PUT /bundles/{id}/account` | | `tokportal_configure_bundle_video` | `PUT /bundles/{id}/videos/{position}` | | `tokportal_batch_configure_bundle_videos` | `PUT /bundles/{id}/videos/batch` | | `tokportal_publish_bundle` | `POST /bundles/{id}/publish` | | `tokportal_get_account_analytics` | `GET /accounts/{id}/analytics` | See the [full MCP tool list](/mcp#available-tools). ## Safe prompt pattern Ask the agent to plan, show the API payload, then wait for confirmation before calling mutating tools: > "Create a draft plan for a US TikTok bundle with 5 videos and niche warming. Show the exact `tokportal_create_bundle` payload and estimated credit cost. Do not execute until I confirm." Then approve once the payload matches the API docs: ```json { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 5, "wants_niche_warming": true, "niche_warming_instructions": "Fitness and gym content.", "external_ref": "openclaw-fitness-us" } ``` ## Realistic workflow 1. Check credits with `tokportal_get_credit_balance`. 2. Create a bundle with `tokportal_create_bundle`. 3. Configure account profile fields with `tokportal_configure_bundle_account`. 4. Configure videos from public URLs or uploaded media with `tokportal_configure_bundle_video`. 5. Publish with `tokportal_publish_bundle`. 6. Monitor bundle status, webhook events, or analytics. The agent should not promise organic reach, view counts, or platform enforcement outcomes. TokPortal exposes operational workflow tools; performance still depends on content, platform behavior, timing, and account state. Related docs: [OpenClaw Integration](/openclaw), [MCP Server](/mcp), [Create Bundle](/create-bundle), [Configure Videos](/configure-videos), [Analytics](/analytics). --- ## TokPortal MCP Server for Cursor Source: use-cases/02-ai-agents/92-cursor-mcp-tiktok.md URL: https://developers.tokportal.com/use-cases/cursor-mcp-tiktok # TokPortal MCP Server for Cursor The public MCP package is `tokportal-mcp`. It lets Cursor call generated TokPortal tools using your API key. This is a local stdio MCP server; your key is passed to the TokPortal API by the local process. ## Install Add this to `.cursor/mcp.json`: ```json { "mcpServers": { "tokportal": { "command": "npx", "args": ["-y", "tokportal-mcp"], "env": { "TOKPORTAL_API_KEY": "sk_your_key_here" } } } } ``` Get your key from the [Developer Portal](https://app.tokportal.com/developer/api-keys). Reference: [MCP Server](/mcp) ## Verify the connection Ask Cursor: > "Use the TokPortal MCP tools to show my credit balance." If connected, Cursor should call `tokportal_get_credit_balance`. ## Create a bundle from Cursor Use a prompt that forces Cursor to show the payload before executing: > "Prepare a TokPortal request for a US TikTok bundle with 10 video slots and niche warming for SaaS/tech content. Show the exact MCP tool call first. Wait for confirmation before running it." Expected tool shape: ```json { "tool": "tokportal_create_bundle", "args": { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 10, "wants_niche_warming": true, "niche_warming_instructions": "SaaS and technology content.", "external_ref": "cursor-saas-us" } } ``` Costs are returned by the API as `credits_charged` and `cost_breakdown`; do not hardcode them in your agent logic. ## Configure videos from project files Cursor can read local files if you allow it. A useful workflow is: > "Read `marketing/video-plan.md`, extract 5 captions and video URLs, then configure positions 1-5 on bundle `bnd_...`. Use `target_publish_date` starting 5 days from today. Show the batch payload before executing." Expected API surface: - `tokportal_batch_configure_bundle_videos` - `PUT /bundles/{id}/videos/batch` - body shape: `{ "videos": [...] }` Reference: [Configure Videos](/configure-videos) ## Monitor status and analytics Useful tools: | Tool | Use | |------|-----| | `tokportal_get_bundle` | Inspect bundle status and configured fields | | `tokportal_get_bundle_publish_readiness` | See why a bundle cannot publish yet | | `tokportal_list_accounts` | List delivered accounts | | `tokportal_get_account_analytics` | Fetch account analytics when available | | `tokportal_list_account_video_analytics` | Fetch post-level analytics | Analytics availability depends on the account/post state and your plan. See [Analytics](/analytics). ## Operational guardrails - Ask Cursor to confirm mutating calls before execution. - Use `external_ref` to connect TokPortal records to your internal campaign IDs. - Do not ask the agent to promise views, engagement, reach, or platform enforcement outcomes. - Use [Webhooks](/webhooks) for production monitoring instead of relying only on chat output. Related docs: [SDKs & CLI](/sdks-cli), [Media Upload](/media-upload), [Python Quickstart](/use-cases/platform-guides/python-quickstart), [Claude Code](/use-cases/ai-agents/claude-code-social-media). --- ## TokPortal + Claude via MCP Source: use-cases/02-ai-agents/95-claude-code-social-media.md URL: https://developers.tokportal.com/use-cases/claude-code-social-media # TokPortal + Claude via MCP Claude-compatible MCP clients can run TokPortal tools through the public `tokportal-mcp` package. The server is generated from the public OpenAPI contract and uses your TokPortal API key. ## Setup Use this MCP server config: ```json { "mcpServers": { "tokportal": { "command": "npx", "args": ["-y", "tokportal-mcp"], "env": { "TOKPORTAL_API_KEY": "sk_your_key_here" } } } } ``` For Claude Desktop, add it to `claude_desktop_config.json`. For Claude Code or another MCP client, add the same server definition to the client's MCP configuration. Reference: [MCP Server](/mcp) ## Good prompts For mutating API calls, ask Claude to show the payload first: > "Use TokPortal MCP. First check my credit balance. Then prepare, but do not execute, a payload for a TikTok bundle in the US with 5 videos, niche warming, and `external_ref` `claude-q2-us`." Expected payload: ```json { "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 5, "wants_niche_warming": true, "niche_warming_instructions": "Tech and SaaS content.", "external_ref": "claude-q2-us" } ``` After confirmation, Claude can call `tokportal_create_bundle`. ## Supported workflows | Workflow | Tools / endpoints | |----------|-------------------| | Check credits | `tokportal_get_credit_balance`, `GET /credits/balance` | | Create bundles | `tokportal_create_bundle`, `POST /bundles` | | Create several bundles in one country | `tokportal_create_bundles_bulk`, `POST /bundles/bulk` | | Configure account profile | `tokportal_configure_bundle_account`, `PUT /bundles/{id}/account` | | Configure videos | `tokportal_configure_bundle_video`, `tokportal_batch_configure_bundle_videos` | | Upload media | `tokportal_upload_video`, `tokportal_upload_video_direct`, `tokportal_upload_image` | | Publish | `tokportal_publish_bundle`, `POST /bundles/{id}/publish` | | Analytics | `tokportal_get_analytics_dashboard`, `tokportal_get_account_analytics`, `tokportal_list_account_video_analytics` | See [MCP available tools](/mcp#available-tools) for the complete generated list. ## CSV and media workflows The CSV import endpoint is multipart: ```http POST /bundles/{id}/videos/import-csv ``` If your MCP client cannot easily provide multipart files, use one of these alternatives: - Configure videos in JSON with `tokportal_batch_configure_bundle_videos`. - Upload media through [Media Upload](/media-upload), then pass returned URLs/paths to video configuration. - Use the [CLI or Node SDK](/sdks-cli) for file-heavy workflows. ## Monitoring For production work, combine Claude with: - [Webhooks](/webhooks) for lifecycle events - `tokportal_get_bundle_publish_readiness` before publishing - [Analytics](/analytics) after posts are live Do not treat AI output as a guarantee of campaign performance. The MCP server executes supported TokPortal API operations; views, reach, and account health depend on platform behavior and content quality. Related guides: [Cursor MCP](/use-cases/ai-agents/cursor-mcp-tiktok), [OpenClaw](/use-cases/ai-agents/openclaw-tiktok-posting), [Python Quickstart](/use-cases/platform-guides/python-quickstart). --- ## Create Instagram Account Bundles with TokPortal API Source: use-cases/03-platform-guides/93-create-instagram-accounts-api.md URL: https://developers.tokportal.com/use-cases/create-instagram-accounts-api # Create Instagram Account Bundles with TokPortal API Use `platform: "instagram"` when creating a TokPortal bundle. The API can create account-only bundles, account-and-video bundles, or videos-only bundles for an existing delivered account. This guide covers the public contract. It does not promise reach, engagement, account immunity, or any specific platform outcome. ## 1. Create an Instagram bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "instagram", "country": "US", "title": "US Instagram launch", "videos_quantity": 3, "external_ref": "ig-launch-us" }' ``` The response includes `data.bundle_id`, `credits_charged`, `credits_remaining`, and `cost_breakdown`. Reference: [Create Bundle](/create-bundle) ## 2. Configure the account profile Account configuration is required before publishing. ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "username": "mybrand.us", "visible_name": "My Brand", "biography": "New drops every week.", "profile_picture_url": "https://cdn.example.com/mybrand/profile.jpg", "link_in_bio": "https://example.com" }' ``` For Instagram, `link_in_bio` is supported. Keep `biography` under 80 characters and use unique usernames/display names across accounts. Reference: [Account Configuration](/account-configuration) ## 3. Configure an Instagram Reel For a Reel video, use `video_type: "video"` and `instagram_content_type: "reel"`. ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/1 \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "instagram_content_type": "reel", "description": "Morning routine with the new collection #style", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/reels/reel-01.mp4", "instagram_audio_name": "Original audio", "external_ref": "ig-reel-01" }' ``` `video_url` can be any public/direct URL or a `public_url` returned by [Media Upload](/media-upload). ## 4. Configure an Instagram carousel post For a swipeable Instagram carousel, use `video_type: "carousel"` and `instagram_content_type: "post"`. Upload images first and use the returned `storage_path` values: ```bash curl -X POST https://app.tokportal.com/api/ext/upload/image/from-url \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://cdn.example.com/carousel/slide-1.jpg", "bundle_id": "bnd_abc123", "purpose": "carousel" }' ``` Then configure the slot: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/2 \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "carousel", "instagram_content_type": "post", "description": "Which look is your favorite?", "target_publish_date": "2026-06-07", "carousel_images": [ "carousel-images/org_xxx/bnd_abc123/slide-1.jpg", "carousel-images/org_xxx/bnd_abc123/slide-2.jpg" ], "instagram_location": "Los Angeles, California" }' ``` Reference: [Configure Videos](/configure-videos) ## Deep warming Deep warming is an Instagram-only option. Enable it with `wants_deep_warming: true`. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "instagram", "country": "US", "videos_quantity": 3, "wants_deep_warming": true, "external_ref": "ig-deep-warming-us" }' ``` `wants_deep_warming` and `wants_niche_warming` are mutually exclusive. The API returns validation errors if incompatible options are sent. ## Bulk Instagram creation `POST /bundles/bulk` accepts one `country` and one or more platforms. To cover several countries, call it once per country. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": ["instagram"], "country": "US", "accounts_count": 5, "upload_accounts_count": 2, "videos_per_account": 6, "wants_deep_warming": true, "external_ref": "ig-bulk-us" }' ``` This creates 5 Instagram bundles in the selected country. Two include video slots; three are account-only. Reference: [Create Bulk](/create-bulk) ## Delivered account access After delivery, accounts are available through the [Delivered Accounts](/saved-accounts) API: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts?platform=instagram" \ -H "X-API-Key: sk_your_key_here" ``` Credential reveal is a separate endpoint and triggers the support disclaimer described in [Delivered Accounts](/saved-accounts): ```bash curl -X POST https://app.tokportal.com/api/ext/accounts/acc_abc123/reveal-credentials \ -H "X-API-Key: sk_your_key_here" ``` Related docs: [Media Upload](/media-upload), [Publish & Unpublish](/publish-unpublish), [Analytics](/analytics), [MCP Server](/mcp). --- ## Order a US TikTok Workflow with TokPortal API Source: use-cases/03-platform-guides/94-post-us-tiktok-from-abroad.md URL: https://developers.tokportal.com/use-cases/post-us-tiktok-from-abroad # Order a US TikTok Workflow with TokPortal API If your team is outside the United States, you can still create a TokPortal bundle with `country: "US"` or `country: "USA"` and `platform: "tiktok"`. TokPortal then handles the order lifecycle for that target country. This is an operational workflow, not a guarantee of reach, views, or immunity from platform restrictions. TikTok distribution and account health depend on content, timing, platform systems, and account state. ## 1. Create a US TikTok bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "title": "US TikTok launch", "videos_quantity": 5, "wants_niche_warming": true, "niche_warming_instructions": "Tech product demos and founder-led content.", "external_ref": "us-tiktok-launch" }' ``` The server calculates credits and returns `credits_charged`, `credits_remaining`, and `cost_breakdown`. Reference: [Create Bundle](/create-bundle) ## 2. Configure the account ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "username": "mybrand_us", "visible_name": "My Brand", "biography": "New product videos.", "profile_picture_url": "https://cdn.example.com/mybrand/profile.jpg", "niche_warming_instructions": "Engage with tech and startup content." }' ``` Reference: [Account Configuration](/account-configuration) ## 3. Configure content ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/1 \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "What we built this week #startup #tech", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/videos/demo-01.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/example-sound-123456789", "external_ref": "demo-01" }' ``` `target_publish_date` must respect the lead-time rules documented in [Configure Videos](/configure-videos). New accounts require more lead time than existing accounts. ## 4. Publish ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/publish \ -H "X-API-Key: sk_your_key_here" ``` If publishing fails, check readiness: ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123/publish-readiness \ -H "X-API-Key: sk_your_key_here" ``` Reference: [Publish & Unpublish](/publish-unpublish) ## Scaling beyond one US account Use [Create Bulk](/create-bulk) to create multiple bundles in the same country: ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": ["tiktok"], "country": "US", "accounts_count": 10, "upload_accounts_count": 4, "videos_per_account": 8, "wants_niche_warming": true, "niche_warming_instructions": "Consumer tech and product demo content.", "external_ref": "us-tiktok-bulk" }' ``` To run the same workflow in other countries, first call: ```bash curl -X GET https://app.tokportal.com/api/ext/countries \ -H "X-API-Key: sk_your_key_here" ``` Then loop over the enabled countries and send one bulk request per country. ## Track performance After the account is delivered and posts are live: ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics \ -H "X-API-Key: sk_your_key_here" ``` For post-level metrics: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics/videos?sort_by=views&sort_order=desc" \ -H "X-API-Key: sk_your_key_here" ``` Reference: [Analytics](/analytics) Related guides: [n8n](/use-cases/no-code/n8n-tiktok-automation), [Bulk TikTok Account Creation](/use-cases/platform-guides/bulk-tiktok-accounts-api), [Python Quickstart](/use-cases/platform-guides/python-quickstart). --- ## Bulk TikTok Bundle Creation API Source: use-cases/03-platform-guides/97-bulk-tiktok-accounts-api.md URL: https://developers.tokportal.com/use-cases/bulk-tiktok-accounts-api # Bulk TikTok Bundle Creation API `POST /bundles/bulk` creates multiple bundles in one request. The current public contract supports: - one `country` per request - one or more `platforms` - up to 100 accounts per platform per request - optional video slots on a subset of accounts - one atomic credit debit for the whole batch For multiple countries, loop over country codes and send one request per country. Reference: [Create Bulk](/create-bulk) ## Request shape ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": ["tiktok"], "country": "US", "accounts_count": 10, "upload_accounts_count": 4, "videos_per_account": 8, "wants_niche_warming": true, "niche_warming_instructions": "Consumer tech and product demo content.", "external_ref": "bulk-us-q2" }' ``` This creates 10 TikTok bundles in the US target country: | Result | Count | |--------|-------| | `account_and_videos` bundles | 4 | | `account_only` bundles | 6 | | Video slots per upload bundle | 8 | | Total video slots | 32 | Credits are returned by the API. Do not calculate costs client-side except for previews; the server is the source of truth. ## Multi-platform bulk `accounts_count` is per platform. ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": ["tiktok", "instagram"], "country": "US", "accounts_count": 5, "upload_accounts_count": 2, "videos_per_account": 6, "external_ref": "bulk-us-multi-platform" }' ``` This creates 10 bundles total: 5 TikTok and 5 Instagram. For each platform, 2 bundles include video slots and 3 are account-only. ## Multi-country rollout The endpoint does not accept a `countries` array. Loop from your application or automation tool: ```bash for country in US UK FR DE; do curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"platforms\": [\"tiktok\"], \"country\": \"$country\", \"accounts_count\": 5, \"upload_accounts_count\": 2, \"videos_per_account\": 8, \"external_ref\": \"bulk-q2-$country\" }" done ``` Validate supported countries before running a loop: ```bash curl -X GET https://app.tokportal.com/api/ext/countries \ -H "X-API-Key: sk_your_key_here" ``` ## Configure videos after creation Each returned bundle has an `id`. Configure slots individually: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/1 \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "video_type": "video", "description": "Launch clip #1", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/videos/launch-01.mp4" }' ``` Or configure several positions in one request: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Launch clip #1", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/videos/launch-01.mp4" }, { "position": 2, "video_type": "video", "description": "Launch clip #2", "target_publish_date": "2026-06-07", "video_url": "https://cdn.example.com/videos/launch-02.mp4" } ] }' ``` Reference: [Configure Videos](/configure-videos) ## CSV import CSV import is multipart file upload, not a `csv_url` JSON field: ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/import-csv \ -H "X-API-Key: sk_your_key_here" \ -F "file=@videos.csv" \ -F "auto_publish=true" ``` Reference: [CSV Import](/csv-import) ## Production notes - Split batches larger than the documented request limits. - Use `external_ref` to connect TokPortal bundles to your internal campaign IDs. - Store every returned `bundle.id`; later video configuration and publish calls are per bundle. - Use [Webhooks](/webhooks) instead of polling for high-volume workflows. - Use [Analytics](/analytics) after accounts and posts are live; analytics availability depends on account/post state and plan. Related guides: [DTC Multi-Market Launch](/use-cases/industry/dtc-multi-market-launch), [Agency Management](/use-cases/industry/agency-multi-client-management), [n8n](/use-cases/no-code/n8n-tiktok-automation). --- ## Python Quickstart - TokPortal API Source: use-cases/03-platform-guides/python-quickstart.md URL: https://developers.tokportal.com/use-cases/python-quickstart # Python Quickstart This guide uses raw HTTP with `requests`. TokPortal also publishes a Node SDK and CLI; Python generated clients may exist locally, but the stable public Python path today is raw HTTP. Reference: [SDKs & CLI](/sdks-cli) ## 1. Install ```bash pip install requests ``` ## 2. Configure auth TokPortal uses the `X-API-Key` header. ```python import os import requests BASE_URL = "https://app.tokportal.com/api/ext" API_KEY = os.environ["TOKPORTAL_API_KEY"] JSON_HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json", } AUTH_HEADERS = { "X-API-Key": API_KEY, } ``` Reference: [Authentication](/authentication) ## 3. Create a bundle ```python def create_bundle() -> str: response = requests.post( f"{BASE_URL}/bundles", headers=JSON_HEADERS, json={ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 1, "title": "Python quickstart", "external_ref": "python-quickstart-001", }, timeout=30, ) response.raise_for_status() payload = response.json() print("credits charged:", payload.get("credits_charged")) return payload["data"]["bundle_id"] ``` Reference: [Create Bundle](/create-bundle) ## 4. Configure account Publishing requires account configuration. ```python def configure_account(bundle_id: str) -> dict: response = requests.put( f"{BASE_URL}/bundles/{bundle_id}/account", headers=JSON_HEADERS, json={ "username": "python_demo_us", "visible_name": "Python Demo", "biography": "Daily product videos.", "profile_picture_url": "https://cdn.example.com/profiles/python-demo.jpg", }, timeout=30, ) response.raise_for_status() return response.json() ``` Reference: [Account Configuration](/account-configuration) ## 5. Provide a video URL If your video is already available at a public/direct URL, use that URL directly as `video_url`. ```python video_url = "https://cdn.example.com/videos/promo.mp4" ``` For local files, use the presigned upload flow. This is usually better for 50-100 MB videos because the binary uploads directly to storage instead of going through your automation tool. ```python def upload_video_presigned(bundle_id: str, filepath: str) -> str: response = requests.post( f"{BASE_URL}/upload/video", headers=JSON_HEADERS, json={ "filename": os.path.basename(filepath), "content_type": "video/mp4", "bundle_id": bundle_id, }, timeout=30, ) response.raise_for_status() upload = response.json()["data"] with open(filepath, "rb") as file_handle: put_response = requests.put( upload["upload_url"], data=file_handle, headers={"Content-Type": upload["content_type"]}, timeout=300, ) put_response.raise_for_status() return upload["public_url"] ``` Reference: [Media Upload](/media-upload) ## 6. Configure the video slot ```python def configure_video(bundle_id: str, video_url: str) -> dict: response = requests.put( f"{BASE_URL}/bundles/{bundle_id}/videos/1", headers=JSON_HEADERS, json={ "video_type": "video", "description": "Built from Python #api", "target_publish_date": "2026-06-05", "video_url": video_url, "external_ref": "python-video-001", }, timeout=30, ) response.raise_for_status() return response.json() ``` Reference: [Configure Videos](/configure-videos) ## 7. Publish ```python def publish_bundle(bundle_id: str) -> dict: response = requests.post( f"{BASE_URL}/bundles/{bundle_id}/publish", headers=AUTH_HEADERS, timeout=30, ) response.raise_for_status() return response.json() ``` If publishing fails, inspect readiness: ```python def get_publish_readiness(bundle_id: str) -> dict: response = requests.get( f"{BASE_URL}/bundles/{bundle_id}/publish-readiness", headers=AUTH_HEADERS, timeout=30, ) response.raise_for_status() return response.json() ``` Reference: [Publish & Unpublish](/publish-unpublish) ## 8. Analytics Analytics are available after the account is delivered and posts have data. ```python def get_account_analytics(account_id: str) -> dict: response = requests.get( f"{BASE_URL}/accounts/{account_id}/analytics", headers=AUTH_HEADERS, timeout=30, ) response.raise_for_status() return response.json() ``` Reference: [Analytics](/analytics) ## Full script ```python import os import requests BASE_URL = "https://app.tokportal.com/api/ext" API_KEY = os.environ["TOKPORTAL_API_KEY"] JSON_HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} AUTH_HEADERS = {"X-API-Key": API_KEY} def create_bundle() -> str: response = requests.post( f"{BASE_URL}/bundles", headers=JSON_HEADERS, json={ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 1, "title": "Python quickstart", "external_ref": "python-quickstart-001", }, timeout=30, ) response.raise_for_status() payload = response.json() print("credits charged:", payload.get("credits_charged")) return payload["data"]["bundle_id"] def configure_account(bundle_id: str) -> None: response = requests.put( f"{BASE_URL}/bundles/{bundle_id}/account", headers=JSON_HEADERS, json={ "username": "python_demo_us", "visible_name": "Python Demo", "biography": "Daily product videos.", "profile_picture_url": "https://cdn.example.com/profiles/python-demo.jpg", }, timeout=30, ) response.raise_for_status() def upload_video_presigned(bundle_id: str, filepath: str) -> str: response = requests.post( f"{BASE_URL}/upload/video", headers=JSON_HEADERS, json={ "filename": os.path.basename(filepath), "content_type": "video/mp4", "bundle_id": bundle_id, }, timeout=30, ) response.raise_for_status() upload = response.json()["data"] with open(filepath, "rb") as file_handle: put_response = requests.put( upload["upload_url"], data=file_handle, headers={"Content-Type": upload["content_type"]}, timeout=300, ) put_response.raise_for_status() return upload["public_url"] def configure_video(bundle_id: str, video_url: str) -> None: response = requests.put( f"{BASE_URL}/bundles/{bundle_id}/videos/1", headers=JSON_HEADERS, json={ "video_type": "video", "description": "Built from Python #api", "target_publish_date": "2026-06-05", "video_url": video_url, "external_ref": "python-video-001", }, timeout=30, ) response.raise_for_status() def publish_bundle(bundle_id: str) -> dict: response = requests.post( f"{BASE_URL}/bundles/{bundle_id}/publish", headers=AUTH_HEADERS, timeout=30, ) response.raise_for_status() return response.json() if __name__ == "__main__": bundle_id = create_bundle() configure_account(bundle_id) local_video = os.environ.get("PROMO_VIDEO_PATH") if local_video: video_url = upload_video_presigned(bundle_id, local_video) else: video_url = os.environ["PROMO_VIDEO_URL"] configure_video(bundle_id, video_url) result = publish_bundle(bundle_id) print(result) ``` Related docs: [Create Bundle](/create-bundle), [Media Upload](/media-upload), [Webhooks](/webhooks), [MCP Server](/mcp). --- ## TokPortal API for Agencies Source: use-cases/04-industry/98-agency-multi-client.md URL: https://developers.tokportal.com/use-cases/agency-multi-client-management # TokPortal API for Agencies Agencies can manage multiple client workflows from one TokPortal organization by using `title`, `external_ref`, webhooks, and analytics exports. The API does not create separate sub-accounts or client billing ledgers by itself; build that mapping in your own system. ## Data model for clients Use your internal client/campaign IDs in `external_ref`. ```json { "client_id": "acme", "campaign_id": "spring-2026", "tokportal_external_ref": "acme-spring-2026-us" } ``` Then send that value when creating bundles. ## Create a client bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "title": "Acme - US TikTok", "videos_quantity": 10, "external_ref": "acme-spring-2026-us" }' ``` Reference: [Create Bundle](/create-bundle) ## Configure account and content Account configuration: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "username": "acme_us", "visible_name": "Acme", "biography": "New launches weekly.", "profile_picture_url": "https://cdn.example.com/acme/profile.jpg" }' ``` Video configuration: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Acme launch clip #1", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/acme/clip-01.mp4", "external_ref": "acme-clip-01" } ] }' ``` References: [Account Configuration](/account-configuration), [Configure Videos](/configure-videos) ## Scale one client across platforms Use bulk creation for one country at a time: ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "platforms": ["tiktok", "instagram"], "country": "US", "accounts_count": 3, "upload_accounts_count": 1, "videos_per_account": 5, "external_ref": "acme-spring-2026-us" }' ``` For multiple countries, loop over countries and send one request per country. Reference: [Create Bulk](/create-bulk) ## List and reconcile client work List bundles by your client/campaign reference: ```bash curl -X GET "https://app.tokportal.com/api/ext/bundles?external_ref=acme-spring-2026-us" \ -H "X-API-Key: sk_your_key_here" ``` Use webhooks to update your internal dashboard: ```bash curl -X POST https://app.tokportal.com/api/ext/webhooks \ -H "X-API-Key: sk_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "url": "https://agency.example.com/webhooks/tokportal", "events": ["bundle.published", "account.finalized", "video.finalized"], "description": "Agency client dashboard" }' ``` Reference: [Webhooks](/webhooks) ## Reporting Use delivered account IDs to pull analytics: ```bash curl -X GET https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics \ -H "X-API-Key: sk_your_key_here" ``` Export video analytics as CSV when your plan supports it: ```bash curl -X GET "https://app.tokportal.com/api/ext/analytics/export/videos?account=acc_abc123" \ -H "X-API-Key: sk_your_key_here" ``` Reference: [Analytics](/analytics) ## Credit tracking Credits are shared across the organization. Use credit history for reconciliation: ```bash curl -X GET "https://app.tokportal.com/api/ext/credits/history?page=1&per_page=50" \ -H "X-API-Key: sk_your_key_here" ``` Reference: [Credits](/credits) Related guides: [Bulk TikTok Account Creation](/use-cases/platform-guides/bulk-tiktok-accounts-api), [UGC Distribution](/use-cases/industry/ugc-distribution-at-scale), [MCP Server](/mcp). --- ## DTC Multi-Market Launch Source: use-cases/04-industry/dtc-multi-market-launch.md URL: https://developers.tokportal.com/use-cases/dtc-multi-market-launch # DTC Multi-Market Launch DTC brands can use TokPortal to coordinate country-specific TikTok and Instagram workflows. The current API pattern is explicit: create bundles per market, configure localized content, publish each bundle, then compare analytics. There is no `countries` array in the public bulk endpoint. Multi-market launches loop over countries. ## 1. Validate target countries ```bash curl -X GET https://app.tokportal.com/api/ext/countries \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Use enabled country codes from the response. `US` and `GB` aliases are accepted, but your API responses may use canonical TokPortal codes. ## 2. Create one bulk batch per country ```bash for country in US UK FR DE; do curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"accounts_count\": 2, \"upload_accounts_count\": 1, \"platforms\": [\"tiktok\", \"instagram\"], \"country\": \"$country\", \"videos_per_account\": 6, \"wants_niche_warming\": true, \"wants_moderation\": true, \"niche_warming_instructions\": \"DTC product demos, reviews, and lifestyle content.\", \"external_ref\": \"spring-launch-$country\" }" done ``` For each country, this creates 2 bundles per platform. One bundle per platform includes video slots. Reference: [Create Bulk](/create-bulk) ## 3. Configure localized content Store your localized copy in your CMS or campaign database, then configure the correct bundle for each market: ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_us_123/videos/batch \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "New drop is live. #newarrival #launch", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/launch/hero.mp4", "external_ref": "spring-launch-us-hero" } ] }' ``` For an Instagram Reel, include `instagram_content_type: "reel"`: ```json { "position": 1, "video_type": "video", "instagram_content_type": "reel", "description": "Nouvelle collection disponible. #nouveaute", "target_publish_date": "2026-06-06", "video_url": "https://cdn.example.com/launch/hero-fr.mp4", "external_ref": "spring-launch-fr-hero" } ``` Reference: [Configure Videos](/configure-videos) ## 4. Publish per bundle Publish each configured bundle: ```bash curl -X POST https://app.tokportal.com/api/ext/bundles/bnd_us_123/publish \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Before publishing at scale, check readiness: ```bash curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_us_123/publish-readiness \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Reference: [Publish & Unpublish](/publish-unpublish) ## 5. Monitor and compare Use webhooks for lifecycle updates: ```bash curl -X POST https://app.tokportal.com/api/ext/webhooks \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhooks/tokportal", "events": ["bundle.published", "account.finalized", "video.finalized"], "description": "DTC launch monitor" }' ``` Use analytics after delivery: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics/videos?sort_by=views&sort_order=desc" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` References: [Webhooks](/webhooks), [Analytics](/analytics) ## Launch checklist - Keep one internal `external_ref` per country/platform campaign. - Store returned bundle IDs immediately. - Configure account profiles before publishing. - Use public URLs or [Media Upload](/media-upload) for video assets. - Localize captions manually; the API does not translate copy for you. - Split larger launches across multiple requests and respect [Rate Limits](/rate-limits). Related guides: [Bulk TikTok Account Creation](/use-cases/platform-guides/bulk-tiktok-accounts-api), [UGC Distribution](/use-cases/industry/ugc-distribution-at-scale), [Agency Management](/use-cases/industry/agency-multi-client-management). --- ## Music Promotion on TikTok with TokPortal Source: use-cases/04-industry/music-promotion-tiktok.md URL: https://developers.tokportal.com/use-cases/music-promotion-tiktok # Music Promotion on TikTok Labels and artists can use TokPortal to organize TikTok content workflows by country. The API can create bundles, configure videos, include a TikTok sound URL, publish, and read analytics. It does not guarantee trending, virality, sound attribution outcomes, or reach. ## Create a music-focused bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 10, "title": "Track seed - US", "wants_niche_warming": true, "niche_warming_instructions": "Music, dance, pop culture, and creator content.", "external_ref": "track-summer-vibes-us" }' ``` Reference: [Create Bundle](/create-bundle) ## Configure videos with a TikTok sound URL ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Studio clip from the new track #newmusic", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/music/studio-clip.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/example-sound-123456789", "external_ref": "summer-vibes-studio-us" }, { "position": 2, "video_type": "video", "description": "Dance idea for the chorus #dance", "target_publish_date": "2026-06-07", "video_url": "https://cdn.example.com/music/dance-idea.mp4", "tiktok_sound_url": "https://www.tiktok.com/music/example-sound-123456789", "external_ref": "summer-vibes-dance-us" } ] }' ``` For TikTok videos, `tiktok_sound_url` is optional. For TikTok carousels, it is required. See the field reference in [Configure Videos](/configure-videos). ## Roll out by country `POST /bundles/bulk` handles one country per request: ```bash for country in US UK FR DE; do curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"accounts_count\": 2, \"upload_accounts_count\": 1, \"platforms\": [\"tiktok\"], \"country\": \"$country\", \"videos_per_account\": 8, \"wants_niche_warming\": true, \"niche_warming_instructions\": \"Music, dance, and creator content.\", \"external_ref\": \"track-summer-vibes-$country\" }" done ``` Reference: [Create Bulk](/create-bulk) ## Content ideas that fit the API - Lyric clips uploaded as `video_url` - Dance prompts with `tiktok_sound_url` - Behind-the-scenes videos - Short artist messages localized by country - Carousel posts where supported by the video configuration contract ## Track performance ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics/videos?sort_by=views&sort_order=desc" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Compare markets with the same metric definitions from [Analytics](/analytics). Treat analytics as measurement, not as a guarantee of future reach. Related docs: [Media Upload](/media-upload), [Configure Videos](/configure-videos), [Analytics](/analytics), [MCP Server](/mcp). --- ## Product Seeding Workflows for TikTok Source: use-cases/04-industry/tiktok-shop-product-seeding.md URL: https://developers.tokportal.com/use-cases/tiktok-shop-product-seeding # Product Seeding Workflows for TikTok E-commerce teams can use TokPortal to coordinate product demo, unboxing, and UGC-style TikTok workflows. The current public video configuration API supports captions, publish dates, media URLs, TikTok sound URLs, carousels, and moderation/warming options. TokPortal does not currently document a public API field for native TikTok Shop product tagging. If your workflow requires product tags, manage that separately from the current TokPortal public API. ## Create a product-focused bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 8, "title": "Skincare demo - US", "wants_niche_warming": true, "niche_warming_instructions": "Beauty, skincare, product review, and unboxing content.", "wants_moderation": true, "moderation_notice": "Check for required disclosure language and avoid unsupported claims.", "external_ref": "skincare-demo-us" }' ``` Reference: [Create Bundle](/create-bundle) ## Configure product content ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Unboxing the new serum. Results vary. #skincare #unboxing", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/skincare/unboxing-01.mp4", "external_ref": "serum-unboxing-us-01" }, { "position": 2, "video_type": "video", "description": "How it fits into a morning routine #grwm", "target_publish_date": "2026-06-07", "video_url": "https://cdn.example.com/skincare/routine-01.mp4", "external_ref": "serum-routine-us-01" } ] }' ``` Reference: [Configure Videos](/configure-videos) ## Scale across buyer markets Create one bulk batch per country: ```bash for country in US UK FR DE; do curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"accounts_count\": 2, \"upload_accounts_count\": 1, \"platforms\": [\"tiktok\"], \"country\": \"$country\", \"videos_per_account\": 6, \"wants_niche_warming\": true, \"wants_moderation\": true, \"niche_warming_instructions\": \"Beauty, product reviews, and shopping-adjacent content.\", \"external_ref\": \"skincare-demo-$country\" }" done ``` Reference: [Create Bulk](/create-bulk) ## Content that fits the current API - Product demos using `video_url` - Unboxings and first impressions - Before/after-style content only where substantiated and compliant - UGC reposts where you have usage rights - Localized captions per country ## Measure results ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` For post-level rows: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics/videos" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Reference: [Analytics](/analytics) Related docs: [Media Upload](/media-upload), [CSV Import](/csv-import), [Webhooks](/webhooks), [UGC Distribution](/use-cases/industry/ugc-distribution-at-scale). --- ## UGC Distribution at Scale Source: use-cases/04-industry/ugc-distribution-at-scale.md URL: https://developers.tokportal.com/use-cases/ugc-distribution-at-scale # UGC Distribution at Scale Brands can use TokPortal to distribute approved creator content through country-specific bundles. TokPortal provides the workflow API; you remain responsible for creator permissions, disclosures, localized copy, and content quality. ## Workflow 1. Collect creator assets and usage rights. 2. Store media at public/direct URLs or upload through [Media Upload](/media-upload). 3. Create one bundle per market/platform, or use bulk creation per country. 4. Configure localized captions and publish dates. 5. Publish and monitor lifecycle events. 6. Compare analytics once posts are live. ## Create a market bundle ```bash curl -X POST https://app.tokportal.com/api/ext/bundles \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "bundle_type": "account_and_videos", "platform": "tiktok", "country": "US", "videos_quantity": 6, "title": "UGC Wave 1 - US", "wants_niche_warming": true, "niche_warming_instructions": "Lifestyle and product review content.", "external_ref": "ugc-wave-1-us" }' ``` Reference: [Create Bundle](/create-bundle) ## Configure UGC videos ```bash curl -X PUT https://app.tokportal.com/api/ext/bundles/bnd_abc123/videos/batch \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "position": 1, "video_type": "video", "description": "Creator review: first week with the product #review", "target_publish_date": "2026-06-05", "video_url": "https://cdn.example.com/ugc/creator-01.mp4", "external_ref": "creator-01-us" }, { "position": 2, "video_type": "video", "description": "Unboxing the new drop #unboxing", "target_publish_date": "2026-06-07", "video_url": "https://cdn.example.com/ugc/creator-02.mp4", "external_ref": "creator-02-us" } ] }' ``` Reference: [Configure Videos](/configure-videos) ## Scale across markets Bulk creation is per country. Loop through target countries: ```bash for country in US UK FR DE; do curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \ -H "X-API-Key: $TOKPORTAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"accounts_count\": 2, \"upload_accounts_count\": 1, \"platforms\": [\"tiktok\", \"instagram\"], \"country\": \"$country\", \"videos_per_account\": 6, \"wants_niche_warming\": true, \"niche_warming_instructions\": \"Lifestyle and product review content.\", \"external_ref\": \"ugc-wave-1-$country\" }" done ``` This creates one country-specific batch at a time. Localize captions and publish dates before configuring each bundle. Reference: [Create Bulk](/create-bulk) ## Track performance by market Use delivered account IDs: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Post-level metrics: ```bash curl -X GET "https://app.tokportal.com/api/ext/accounts/acc_abc123/analytics/videos?sort_by=views&sort_order=desc" \ -H "X-API-Key: $TOKPORTAL_API_KEY" ``` Reference: [Analytics](/analytics) ## Practical notes - Use `external_ref` to map each video to creator, market, and campaign. - Avoid reusing identical captions everywhere; localize language and hashtags. - Spread publish dates using `target_publish_date`. - Use [Webhooks](/webhooks) to update your internal content tracker. - Do not use the API copy to imply guaranteed reach or platform safety. Related guides: [Agency Management](/use-cases/industry/agency-multi-client-management), [DTC Multi-Market Launch](/use-cases/industry/dtc-multi-market-launch), [Media Upload](/media-upload). --- ## TokPortal Use Cases & Integrations Source: use-cases/index.md URL: https://developers.tokportal.com/use-cases/ # Use Cases & Integrations These guides show realistic ways to use the public TokPortal API. They are implementation recipes, not separate products: no-code tools call the REST API through HTTP modules, AI tools use the published MCP server, and custom apps can use raw HTTP, the Node SDK, or the CLI. Core references: - [Authentication](/authentication) - send `X-API-Key` on every API request - [Create Bundle](/create-bundle) - create account/video bundles - [Create Bulk](/create-bulk) - create multiple bundles for one country and one or more platforms - [Configure Videos](/configure-videos) - configure captions, dates, video URLs, carousels, and platform fields - [Media Upload](/media-upload) - direct upload, presigned upload, and URL-based media ingestion - [Webhooks](/webhooks) - receive lifecycle events - [SDKs & CLI](/sdks-cli) - public packages available today - [MCP Server](/mcp) - AI tool integration via `tokportal-mcp` ## No-Code Automation TokPortal does not currently ship native n8n, Zapier, or Make apps. Use their HTTP modules with `X-API-Key`. | Guide | What you'll build | |-------|-------------------| | [n8n + TikTok Automation](/use-cases/no-code/n8n-tiktok-automation) | HTTP workflow: create bundle, configure account/video, publish | | [Zapier + Social Media](/use-cases/no-code/zapier-social-media-automation) | Zapier webhooks flow from Sheets, Airtable, Dropbox, or a CMS | | [Make + TikTok](/use-cases/no-code/make-tiktok-automation) | Visual scenario for one or more country-specific API calls | ## AI Agents Use the published MCP server to expose generated TokPortal tools to MCP-compatible assistants. | Guide | What you'll build | |-------|-------------------| | [OpenClaw AI Agent](/use-cases/ai-agents/openclaw-tiktok-posting) | Natural-language TokPortal workflow using the OpenClaw skill path | | [Cursor MCP Server](/use-cases/ai-agents/cursor-mcp-tiktok) | Create bundles and configure videos from Cursor via `tokportal-mcp` | | [Claude Code + TokPortal](/use-cases/ai-agents/claude-code-social-media) | MCP-powered campaign operations from Claude-compatible tools | ## Platform Guides Focused guides for common API surfaces. | Guide | What you'll learn | |-------|-------------------| | [Create Instagram Accounts via API](/use-cases/platform-guides/create-instagram-accounts-api) | Instagram account bundles, profile configuration, Reels, posts, and carousels | | [Post to US TikTok from Abroad](/use-cases/platform-guides/post-us-tiktok-from-abroad) | How to order a US-targeted TikTok workflow without promising reach outcomes | | [Bulk TikTok Account Creation](/use-cases/platform-guides/bulk-tiktok-accounts-api) | Correct bulk request shape and scaling limits | | [Python Quickstart](/use-cases/platform-guides/python-quickstart) | Raw Python `requests` workflow with `X-API-Key` auth | ## Industry Use Cases These are operational playbooks that reuse the same API primitives. | Guide | Who it's for | |-------|--------------| | [Agency Multi-Client Management](/use-cases/industry/agency-multi-client-management) | Agencies grouping bundles by client with `title` and `external_ref` | | [UGC Distribution at Scale](/use-cases/industry/ugc-distribution-at-scale) | Brands distributing approved creator content across target markets | | [Music & Label Promotion](/use-cases/industry/music-promotion-tiktok) | Labels and artists scheduling content that uses a TikTok sound URL | | [TikTok Shop Product Seeding](/use-cases/industry/tiktok-shop-product-seeding) | E-commerce teams publishing product content without claiming native Shop tagging | | [DTC Multi-Market Launch](/use-cases/industry/dtc-multi-market-launch) | DTC teams creating one API batch per market and comparing analytics | Need the full contract? Start with [Getting Started](/getting-started) or the [OpenAPI reference](/api-reference). --- # OpenAPI Summary ```json { "openapi": "3.1.0", "info": { "title": "TokPortal Public API", "version": "2026-05-25", "description": "Non-breaking public API contract for TokPortal developer integrations. This spec is the first generated surface for SDKs, CLI, MCP, and docs." }, "servers": [ { "url": "https://app.tokportal.com/api/ext", "description": "Production" } ], "security": [ { "ApiKeyAuth": [] } ], "tags": [ { "name": "Profile" }, { "name": "Reference" }, { "name": "Credits" }, { "name": "Bundles" }, { "name": "Account Configuration" }, { "name": "Videos" }, { "name": "Accounts" }, { "name": "Uploads" }, { "name": "Analytics" }, { "name": "Comments" }, { "name": "Webhooks" } ], "paths": { "/me": { "get": { "operationId": "getCurrentUser", "summary": "Get authenticated user", "tags": [ "Profile" ], "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "description": "Authenticated user profile and credit balance.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MeResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/me\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/me', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/me',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/me\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/countries": { "get": { "operationId": "listCountries", "summary": "List available countries", "tags": [ "Reference" ], "security": [], "responses": { "200": { "description": "Enabled country codes available for account creation.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/countries\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/countries', {\n method: 'GET',\n headers: {\n \n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/countries',\n headers={}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/countries\", nil)\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/platforms": { "get": { "operationId": "listPlatforms", "summary": "List available platforms", "tags": [ "Reference" ], "security": [], "responses": { "200": { "description": "Supported social platforms.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/platforms\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/platforms', {\n method: 'GET',\n headers: {\n \n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/platforms',\n headers={}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/platforms\", nil)\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/credit-costs": { "get": { "operationId": "getCreditCosts", "summary": "Get credit pricing", "tags": [ "Credits" ], "security": [], "responses": { "200": { "description": "Current credit cost table.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/credit-costs\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/credit-costs', {\n method: 'GET',\n headers: {\n \n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/credit-costs',\n headers={}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/credit-costs\", nil)\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/credits/balance": { "get": { "operationId": "getCreditBalance", "summary": "Get credit balance", "tags": [ "Credits" ], "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "description": "Current credit balance.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/credits/balance\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/credits/balance', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/credits/balance',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/credits/balance\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/credits/history": { "get": { "operationId": "listCreditTransactions", "summary": "List credit transactions", "tags": [ "Credits" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } } ], "responses": { "200": { "description": "Paginated credit transaction history.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/credits/history?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles": { "get": { "operationId": "listBundles", "summary": "List bundles", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, { "name": "status", "in": "query", "required": false, "description": "Filter by bundle status.", "schema": { "type": "string" } }, { "name": "bundle_type", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "account_only", "account_and_videos", "videos_only" ] } }, { "name": "platform", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "tiktok", "instagram" ] } }, { "name": "external_ref", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "account_status", "in": "query", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Paginated bundle list.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "post": { "operationId": "createBundle", "summary": "Create a bundle", "description": "Creates an account-only, account-and-videos, or videos-only bundle. Credit cost is calculated server-side.", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBundleRequest" } } } }, "responses": { "201": { "description": "Bundle created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBundleResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid request body or business rule violation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Duplicate or invalid state.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "429": { "description": "Create rate limit exceeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"bundle_type\": \"account_only\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"bundle_type\": \"account_only\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"bundle_type\": \"account_only\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles\", strings.NewReader(\"{\\n \\\"bundle_type\\\": \\\"account_only\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/bulk": { "post": { "operationId": "createBundlesBulk", "summary": "Create bundles in bulk", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBulkBundlesRequest" } } } }, "responses": { "201": { "description": "Bulk bundle creation result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid request body or business rule violation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "429": { "description": "Create rate limit exceeded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/bulk\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"platforms\": [\n \"tiktok\"\n ],\n \"country\": \"USA\",\n \"accounts_count\": 25\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/bulk', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"platforms\": [\n \"tiktok\"\n ],\n \"country\": \"USA\",\n \"accounts_count\": 25\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/bulk',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"platforms\": [\n \"tiktok\"\n ],\n \"country\": \"USA\",\n \"accounts_count\": 25\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/bulk\", strings.NewReader(\"{\\n \\\"platforms\\\": [\\n \\\"tiktok\\\"\\n ],\\n \\\"country\\\": \\\"USA\\\",\\n \\\"accounts_count\\\": 25\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}": { "get": { "operationId": "getBundle", "summary": "Get a bundle", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Bundle details.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "patch": { "operationId": "updateBundle", "summary": "Update bundle settings", "description": "Updates mutable bundle metadata such as title, external_ref, and auto_finalize_videos.", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchBundleRequest" } } } }, "responses": { "200": { "description": "Bundle updated.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid patch body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "403": { "description": "Bundle belongs to another account.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Duplicate external_ref.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PATCH \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"auto_finalize_videos\": false,\n \"external_ref\": \"external_ref\",\n \"title\": \"Launch campaign\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'PATCH',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"auto_finalize_videos\": false,\n \"external_ref\": \"external_ref\",\n \"title\": \"Launch campaign\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PATCH',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"auto_finalize_videos\": false,\n \"external_ref\": \"external_ref\",\n \"title\": \"Launch campaign\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PATCH\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", strings.NewReader(\"{\\n \\\"auto_finalize_videos\\\": false,\\n \\\"external_ref\\\": \\\"external_ref\\\",\\n \\\"title\\\": \\\"Launch campaign\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/publish": { "post": { "operationId": "publishBundle", "summary": "Publish a bundle", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Publish result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Bundle is not ready or is in an invalid state.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/publish-readiness": { "get": { "operationId": "getBundlePublishReadiness", "summary": "Check bundle publish readiness", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Readiness blockers and ready flag.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/publish-readiness\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/unpublish": { "post": { "operationId": "unpublishBundle", "summary": "Unpublish a bundle", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Unpublish result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Bundle is in an invalid state.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/unpublish\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/add-video-slots": { "post": { "operationId": "addVideoSlots", "summary": "Add video slots to a bundle", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuantityRequest" } } } }, "responses": { "200": { "description": "Created video slots.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid quantity or bundle type.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"quantity\": 1\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"quantity\": 1\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"quantity\": 1\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-video-slots\", strings.NewReader(\"{\\n \\\"quantity\\\": 1\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/add-edit-slots": { "post": { "operationId": "addEditSlots", "summary": "Add edit slots to a bundle", "tags": [ "Bundles" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuantityRequest" } } } }, "responses": { "200": { "description": "Created edit slots.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid quantity.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"quantity\": 1\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"quantity\": 1\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"quantity\": 1\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/add-edit-slots\", strings.NewReader(\"{\\n \\\"quantity\\\": 1\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/account": { "get": { "operationId": "getBundleAccount", "summary": "Get bundle account configuration", "tags": [ "Account Configuration" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Account configuration.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Bundle or account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "put": { "operationId": "configureBundleAccount", "summary": "Configure bundle account profile", "tags": [ "Account Configuration" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfigureAccountRequest" } } } }, "responses": { "200": { "description": "Configured account.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid account profile.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Account status does not allow configuration.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PUT \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"username\": \"username\",\n \"visible_name\": \"visible_name\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account', {\n method: 'PUT',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"username\": \"username\",\n \"visible_name\": \"visible_name\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PUT',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"username\": \"username\",\n \"visible_name\": \"visible_name\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PUT\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account\", strings.NewReader(\"{\\n \\\"username\\\": \\\"username\\\",\\n \\\"visible_name\\\": \\\"visible_name\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/account/corrections": { "post": { "operationId": "requestBundleAccountCorrections", "summary": "Request account corrections", "description": "Moves an in-review account back to pending corrections with reviewer feedback.", "tags": [ "Account Configuration" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountCorrectionsRequest" } } } }, "responses": { "200": { "description": "Account corrections requested.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid correction body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Account is not in review.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"comment\": \"Please review this item.\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"comment\": \"Please review this item.\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"comment\": \"Please review this item.\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/corrections\", strings.NewReader(\"{\\n \\\"comment\\\": \\\"Please review this item.\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/account/finalize": { "post": { "operationId": "finalizeBundleAccount", "summary": "Finalize account review", "description": "Approves an in-review account and marks it finalized.", "tags": [ "Account Configuration" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Account finalized.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Account is not in review.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/account/finalize\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos": { "get": { "operationId": "listBundleVideos", "summary": "List bundle video slots", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Video slots for the bundle.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}": { "get": { "operationId": "getBundleVideo", "summary": "Get video slot configuration", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Video slot configuration.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "put": { "operationId": "configureBundleVideo", "summary": "Configure a video slot", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConfigureVideoRequest" } } } }, "responses": { "200": { "description": "Configured video slot.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid video metadata.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video status does not allow configuration.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PUT \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', {\n method: 'PUT',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PUT',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PUT\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\", strings.NewReader(\"{\\n \\\"video_type\\\": \\\"video\\\",\\n \\\"description\\\": \\\"Campaign content description.\\\",\\n \\\"target_publish_date\\\": \\\"target_publish_date\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] }, "patch": { "operationId": "patchBundleVideo", "summary": "Patch video references", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchVideoRequest" } } } }, "responses": { "200": { "description": "Patched video slot.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid patch body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PATCH \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"external_ref\": \"external_ref\",\n \"name\": \"name\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1', {\n method: 'PATCH',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"external_ref\": \"external_ref\",\n \"name\": \"name\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PATCH',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"external_ref\": \"external_ref\",\n \"name\": \"name\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PATCH\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1\", strings.NewReader(\"{\\n \\\"external_ref\\\": \\\"external_ref\\\",\\n \\\"name\\\": \\\"name\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/batch": { "put": { "operationId": "batchConfigureBundleVideos", "summary": "Configure video slots in bulk", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchConfigureVideosRequest" } } } }, "responses": { "200": { "description": "Batch video configuration result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid batch body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PUT \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"videos\": [\n {\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\",\n \"position\": 1\n }\n ]\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch', {\n method: 'PUT',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"videos\": [\n {\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\",\n \"position\": 1\n }\n ]\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PUT',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"videos\": [\n {\n \"video_type\": \"video\",\n \"description\": \"Campaign content description.\",\n \"target_publish_date\": \"target_publish_date\",\n \"position\": 1\n }\n ]\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PUT\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/batch\", strings.NewReader(\"{\\n \\\"videos\\\": [\\n {\\n \\\"video_type\\\": \\\"video\\\",\\n \\\"description\\\": \\\"Campaign content description.\\\",\\n \\\"target_publish_date\\\": \\\"target_publish_date\\\",\\n \\\"position\\\": 1\\n }\\n ]\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/publish-all": { "post": { "operationId": "publishAllBundleVideos", "summary": "Publish all configured videos on an active bundle", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Published video slots.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Bundle status does not allow publishing videos.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/publish-all\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/import-csv": { "post": { "operationId": "importBundleVideosCsv", "summary": "Import video slots from CSV", "description": "Uploads a CSV file, downloads referenced media, and configures available video slots.", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": [ "file" ], "properties": { "file": { "type": "string", "format": "binary" }, "auto_publish": { "type": "boolean", "default": false } } } } } }, "responses": { "200": { "description": "CSV import result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid CSV or upload body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -F \"file=@/path/to/file\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/import-csv\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/publish": { "post": { "operationId": "publishBundleVideo", "summary": "Publish one video slot", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Video slot published.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Bundle or video status does not allow publishing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/publish\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/reset": { "post": { "operationId": "resetBundleVideo", "summary": "Reset one video slot", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Video slot reset.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video status does not allow reset.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/reset\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/unschedule": { "post": { "operationId": "unscheduleBundleVideo", "summary": "Unschedule one video slot", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Video slot unscheduled.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video status does not allow unscheduling.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/unschedule\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/finalize": { "post": { "operationId": "finalizeBundleVideo", "summary": "Finalize video review", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "responses": { "200": { "description": "Video slot finalized.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video is not in review.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/finalize\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/corrections": { "post": { "operationId": "requestBundleVideoCorrections", "summary": "Request video corrections", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VideoCorrectionsRequest" } } } }, "responses": { "200": { "description": "Video corrections requested.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid correction body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video is not in review.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"comment\": \"Please review this item.\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"comment\": \"Please review this item.\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"comment\": \"Please review this item.\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/corrections\", strings.NewReader(\"{\\n \\\"comment\\\": \\\"Please review this item.\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/bundles/{id}/videos/{position}/fix-download": { "post": { "operationId": "fixBundleVideoDownload", "summary": "Fix a broken video download", "description": "Replaces a manager-flagged broken video or carousel download URL and clears the download issue flag.", "tags": [ "Videos" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Bundle ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "position", "in": "path", "required": true, "description": "1-based video slot position.", "schema": { "type": "integer", "minimum": 1 } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FixVideoDownloadRequest" } } } }, "responses": { "200": { "description": "Video download issue fixed.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid replacement media body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle or video slot not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Video does not have a download issue.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '\"value\"'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(\"value\")\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json=\"value\"\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/videos/1/fix-download\", strings.NewReader(\"\\\"value\\\"\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts": { "get": { "operationId": "listAccounts", "summary": "List delivered accounts", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } } ], "responses": { "200": { "description": "Paginated delivered account list.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}": { "get": { "operationId": "getAccount", "summary": "Get a delivered account", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Delivered account details.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/bundles": { "get": { "operationId": "listAccountBundles", "summary": "List bundles for a delivered account", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, { "name": "status", "in": "query", "required": false, "description": "Filter by bundle status.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Paginated bundles for account.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/bundles?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/verification-code": { "post": { "operationId": "retrieveAccountVerificationCode", "summary": "Retrieve latest account verification code", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Verification code result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Owning fee required.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Account or verification code not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/reveal-credentials": { "post": { "operationId": "revealAccountCredentials", "summary": "Reveal delivered account credentials", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Credentials reveal result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Owning fee required.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/analytics/can-refresh": { "get": { "operationId": "canRefreshAccountAnalytics", "summary": "Check analytics refresh availability", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Manual refresh availability.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/can-refresh\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/analytics/refresh": { "post": { "operationId": "refreshAccountAnalytics", "summary": "Refresh account analytics", "description": "Backward-compatible account analytics refresh path. Supports forced refresh and post import options.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefreshAnalyticsRequest" } } } }, "responses": { "200": { "description": "Refresh result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "429": { "description": "Manual refresh cooldown active.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "502": { "description": "Refresh provider failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/refresh\", strings.NewReader(\"{\\n \\\"force\\\": false,\\n \\\"includePosts\\\": false,\\n \\\"includeComments\\\": false,\\n \\\"forcePosts\\\": false\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/edit-request": { "get": { "operationId": "getAccountEditRequest", "summary": "Get active account edit request", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Active edit request.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "post": { "operationId": "createAccountEditRequest", "summary": "Request profile edits for a delivered account", "tags": [ "Accounts" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountEditRequest" } } } }, "responses": { "201": { "description": "Edit request created.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid edit request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "An active edit request already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"requested_username\": \"requested_username\",\n \"requested_visible_name\": \"requested_visible_name\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"requested_username\": \"requested_username\",\n \"requested_visible_name\": \"requested_visible_name\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"requested_username\": \"requested_username\",\n \"requested_visible_name\": \"requested_visible_name\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/edit-request\", strings.NewReader(\"{\\n \\\"requested_username\\\": \\\"requested_username\\\",\\n \\\"requested_visible_name\\\": \\\"requested_visible_name\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks/events": { "get": { "operationId": "listWebhookEvents", "summary": "List webhook event catalog", "description": "Returns the supported webhook event types, delivery envelope, signature scheme, and example payloads. This endpoint is public so teams can inspect webhook contracts before creating an API key.", "tags": [ "Webhooks" ], "security": [], "responses": { "200": { "description": "Supported webhook events and delivery contract.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEventsResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/webhooks/events\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/events', {\n method: 'GET',\n headers: {\n \n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/webhooks/events',\n headers={}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/webhooks/events\", nil)\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks": { "get": { "operationId": "listWebhookEndpoints", "summary": "List webhook endpoints", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, { "name": "enabled", "in": "query", "required": false, "description": "Filter by enabled state.", "schema": { "type": "boolean" } }, { "name": "event", "in": "query", "required": false, "description": "Filter endpoints subscribed to an event.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Paginated webhook endpoint list.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/webhooks?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "post": { "operationId": "createWebhookEndpoint", "summary": "Create a webhook endpoint", "description": "Creates a webhook endpoint and returns its signing secret once. Store the secret to verify TokPortal webhook signatures.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookEndpointRequest" } } } }, "responses": { "201": { "description": "Webhook endpoint created.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid webhook endpoint.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Endpoint already exists.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/webhooks\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ]\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ]\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/webhooks',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ]\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/webhooks\", strings.NewReader(\"{\\n \\\"url\\\": \\\"https://example.com/resource\\\",\\n \\\"events\\\": [\\n \\\"webhook.test\\\"\\n ]\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks/{id}": { "get": { "operationId": "getWebhookEndpoint", "summary": "Get a webhook endpoint", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Webhook endpoint details.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Webhook endpoint not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "patch": { "operationId": "updateWebhookEndpoint", "summary": "Update a webhook endpoint", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateWebhookEndpointRequest" } } } }, "responses": { "200": { "description": "Webhook endpoint updated.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid webhook endpoint patch.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Webhook endpoint not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X PATCH \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ],\n \"description\": \"Campaign content description.\",\n \"enabled\": false\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'PATCH',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ],\n \"description\": \"Campaign content description.\",\n \"enabled\": false\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'PATCH',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"url\": \"https://example.com/resource\",\n \"events\": [\n \"webhook.test\"\n ],\n \"description\": \"Campaign content description.\",\n \"enabled\": false\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"PATCH\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", strings.NewReader(\"{\\n \\\"url\\\": \\\"https://example.com/resource\\\",\\n \\\"events\\\": [\\n \\\"webhook.test\\\"\\n ],\\n \\\"description\\\": \\\"Campaign content description.\\\",\\n \\\"enabled\\\": false\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] }, "delete": { "operationId": "deleteWebhookEndpoint", "summary": "Delete a webhook endpoint", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "204": { "description": "Webhook endpoint deleted.", "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Webhook endpoint not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X DELETE \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'DELETE',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'DELETE',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"DELETE\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks/{id}/deliveries": { "get": { "operationId": "listWebhookDeliveries", "summary": "List webhook deliveries", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, { "name": "event_type", "in": "query", "required": false, "description": "Filter by event type.", "schema": { "type": "string" } }, { "name": "success", "in": "query", "required": false, "description": "Filter by delivery success.", "schema": { "type": "boolean" } } ], "responses": { "200": { "description": "Paginated delivery attempts for the webhook endpoint.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Webhook endpoint not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks/{id}/deliveries/{delivery_id}/retry": { "post": { "operationId": "retryWebhookDelivery", "summary": "Retry a webhook delivery", "description": "Resends the stored webhook payload to the endpoint's current URL with a fresh TokPortal-Signature header. The event ID is preserved so receivers can keep idempotent processing.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "delivery_id", "in": "path", "required": true, "description": "Webhook delivery ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Retry delivery result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Webhook endpoint or delivery not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Webhook endpoint is disabled or payload is unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/deliveries/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/retry\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/webhooks/{id}/test": { "post": { "operationId": "testWebhookEndpoint", "summary": "Send a test webhook", "description": "Sends a signed webhook.test event to the endpoint and records the delivery result.", "tags": [ "Webhooks" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Webhook endpoint ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Test delivery result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Webhook endpoint not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/webhooks/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/test\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/upload/video": { "post": { "operationId": "uploadVideo", "summary": "Create a video upload URL", "tags": [ "Uploads" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadVideoRequest" } } } }, "responses": { "200": { "description": "Presigned video upload URL.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid upload request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/upload/video\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"filename\": \"media.mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/upload/video', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"filename\": \"media.mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/upload/video',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"filename\": \"media.mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/upload/video\", strings.NewReader(\"{\\n \\\"filename\\\": \\\"media.mp4\\\",\\n \\\"bundle_id\\\": \\\"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/upload/video/direct": { "post": { "operationId": "uploadVideoDirect", "summary": "Upload a video file directly", "description": "Uploads multipart/form-data directly through TokPortal and returns the public video URL.", "tags": [ "Uploads" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": [ "file", "bundle_id" ], "properties": { "file": { "type": "string", "format": "binary" }, "bundle_id": { "type": "string", "format": "uuid" } } } } } }, "responses": { "200": { "description": "Uploaded video URL.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid upload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/upload/video/direct\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -F \"file=@/path/to/file\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/upload/video/direct', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/upload/video/direct',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/upload/video/direct\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/upload/image": { "post": { "operationId": "uploadImage", "summary": "Create an image upload URL", "tags": [ "Uploads" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadImageRequest" } } } }, "responses": { "200": { "description": "Signed image upload URL.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid upload request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/upload/image\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"filename\": \"media.mp4\",\n \"content_type\": \"video/mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/upload/image', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"filename\": \"media.mp4\",\n \"content_type\": \"video/mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/upload/image',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"filename\": \"media.mp4\",\n \"content_type\": \"video/mp4\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/upload/image\", strings.NewReader(\"{\\n \\\"filename\\\": \\\"media.mp4\\\",\\n \\\"content_type\\\": \\\"video/mp4\\\",\\n \\\"bundle_id\\\": \\\"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/upload/image/direct": { "post": { "operationId": "uploadImageDirect", "summary": "Upload an image file directly", "description": "Uploads multipart/form-data directly through TokPortal and returns storage details. HEIF/HEIC may be converted to JPEG.", "tags": [ "Uploads" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": [ "file", "bundle_id" ], "properties": { "file": { "type": "string", "format": "binary" }, "bundle_id": { "type": "string", "format": "uuid" }, "purpose": { "type": "string", "enum": [ "carousel", "profile_picture" ], "default": "carousel" } } } } } }, "responses": { "200": { "description": "Uploaded image URL.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid upload.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/upload/image/direct\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -F \"file=@/path/to/file\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/upload/image/direct', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/upload/image/direct',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/upload/image/direct\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/upload/image/from-url": { "post": { "operationId": "uploadImageFromUrl", "summary": "Import an image from URL", "description": "Fetches a public direct image URL and stores it permanently in TokPortal storage.", "tags": [ "Uploads" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadImageFromUrlRequest" } } } }, "responses": { "200": { "description": "Stored image details.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid image URL or request body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Bundle not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/upload/image/from-url\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"url\": \"https://example.com/resource\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/upload/image/from-url', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"url\": \"https://example.com/resource\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/upload/image/from-url',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"url\": \"https://example.com/resource\",\n \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/upload/image/from-url\", strings.NewReader(\"{\\n \\\"url\\\": \\\"https://example.com/resource\\\",\\n \\\"bundle_id\\\": \\\"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics": { "get": { "operationId": "getAnalyticsDashboard", "summary": "Get analytics dashboard", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "description": "Analytics dashboard data.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/contract": { "get": { "operationId": "getAnalyticsContract", "summary": "Get analytics data contract", "description": "Returns the Analytics v2 contract, current access payload, metric semantics, freshness targets, and redaction rules.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "description": "Analytics contract and access payload.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/contract\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/contract', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/contract',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/contract\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/export/videos": { "get": { "operationId": "exportAnalyticsVideos", "summary": "Export analytics videos CSV", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "account", "in": "query", "required": false, "description": "Repeatable account filter.", "schema": { "type": "string" } }, { "name": "workspace", "in": "query", "required": false, "schema": { "type": "string", "format": "uuid" } }, { "name": "platform", "in": "query", "required": false, "description": "Repeatable platform filter.", "schema": { "type": "string", "enum": [ "tiktok", "instagram" ] } }, { "name": "country", "in": "query", "required": false, "description": "Repeatable country filter.", "schema": { "type": "string" } }, { "name": "q", "in": "query", "required": false, "description": "Search query.", "schema": { "type": "string" } }, { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } }, { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } } ], "responses": { "200": { "description": "CSV export of analytics video rows.", "content": { "text/csv": { "schema": { "type": "string" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/export/videos?account=example&workspace=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/export/reports": { "post": { "operationId": "createAnalyticsReport", "summary": "Create analytics web report", "description": "Creates a shareable Analytics v2 web report and returns its token and URL.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAnalyticsReportRequest" } } } }, "responses": { "200": { "description": "Created analytics report.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid report request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/analytics/export/reports\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/reports', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/analytics/export/reports',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/analytics/export/reports\", strings.NewReader(\"{\\n \\\"title\\\": \\\"Launch campaign\\\",\\n \\\"template\\\": \\\"template\\\",\\n \\\"brandName\\\": \\\"brandName\\\",\\n \\\"brandAccent\\\": \\\"brandAccent\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/export/reports/html": { "post": { "operationId": "exportAnalyticsReportHtml", "summary": "Export analytics report HTML", "description": "Creates a standalone downloadable HTML analytics report.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAnalyticsReportRequest" } } } }, "responses": { "200": { "description": "Standalone analytics report HTML.", "content": { "text/html": { "schema": { "type": "string" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid report request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/analytics/export/reports/html\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/export/reports/html', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/analytics/export/reports/html',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"title\": \"Launch campaign\",\n \"template\": \"template\",\n \"brandName\": \"brandName\",\n \"brandAccent\": \"brandAccent\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/analytics/export/reports/html\", strings.NewReader(\"{\\n \\\"title\\\": \\\"Launch campaign\\\",\\n \\\"template\\\": \\\"template\\\",\\n \\\"brandName\\\": \\\"brandName\\\",\\n \\\"brandAccent\\\": \\\"brandAccent\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/series": { "get": { "operationId": "getAnalyticsSeries", "summary": "Get analytics time series", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "metric", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "views", "likes", "comments", "shares", "followers" ], "default": "views" } }, { "name": "granularity", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "day", "week" ], "default": "day" } }, { "name": "mode", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "cumulative", "gained", "snapshot" ], "default": "cumulative" } }, { "name": "account", "in": "query", "required": false, "description": "Repeatable account filter.", "schema": { "type": "string" } }, { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } }, { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } } ], "responses": { "200": { "description": "Analytics time series.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/series?metric=example&granularity=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/accounts/{id}": { "get": { "operationId": "getAnalyticsAccount", "summary": "Get account analytics drilldown", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Account analytics drilldown.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/accounts/{id}/refresh": { "post": { "operationId": "refreshAnalyticsAccount", "summary": "Refresh analytics account", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefreshAnalyticsRequest" } } } }, "responses": { "200": { "description": "Refresh result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "429": { "description": "Manual refresh cooldown active.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "502": { "description": "Refresh provider failed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"force\": false,\n \"includePosts\": false,\n \"includeComments\": false,\n \"forcePosts\": false\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh\", strings.NewReader(\"{\\n \\\"force\\\": false,\\n \\\"includePosts\\\": false,\\n \\\"includeComments\\\": false,\\n \\\"forcePosts\\\": false\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/accounts/{id}/raw": { "get": { "operationId": "listAnalyticsAccountRawSnapshots", "summary": "List raw account analytics snapshots", "description": "Returns owner-scoped stored raw provider payloads for a saved account. Full analytics tier only.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "source", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "bundle_social", "apify", "manual" ] } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 30, "default": 5 } }, { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date-time" } } ], "responses": { "200": { "description": "Raw account analytics snapshots.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include raw analytics payloads.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/analytics": { "get": { "operationId": "getAccountAnalytics", "summary": "Get account analytics compatibility view", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Account analytics data.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/accounts/{id}/analytics/videos": { "get": { "operationId": "listAccountVideoAnalytics", "summary": "List post analytics for an account", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }, { "name": "sort_by", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "views", "likes", "engagement_rate", "upload_date" ], "default": "upload_date" } }, { "name": "sort_order", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "asc", "desc" ], "default": "desc" } } ], "responses": { "200": { "description": "Post analytics list.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics/videos?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/videos/{id}/analytics": { "get": { "operationId": "getVideoAnalytics", "summary": "Get single video analytics", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Video ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Video analytics.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Video not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/analytics\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/comments": { "get": { "operationId": "getCommentPulse", "summary": "Get comment pulse analytics", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "platform", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "country", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "account", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "post", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 40, "default": 24 } }, { "name": "postLimit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 10, "default": 6 } }, { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } }, { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } } ], "responses": { "200": { "description": "Comment pulse analytics.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/comments?platform=example&country=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/accounts/{id}/comments": { "get": { "operationId": "listAnalyticsAccountComments", "summary": "List comments for an account post", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Saved account ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "trackedPostId", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "postId", "in": "query", "required": false, "schema": { "type": "string" } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } } ], "responses": { "200": { "description": "Post comments.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include this analytics level.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Account or post not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/comments?trackedPostId=example&postId=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/analytics/posts/{id}/raw": { "get": { "operationId": "listAnalyticsPostRawSnapshots", "summary": "List raw post analytics snapshots", "description": "Returns owner-scoped stored raw provider payloads for a tracked post. Full analytics tier only.", "tags": [ "Analytics" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Tracked post ID.", "schema": { "type": "string", "format": "uuid" } }, { "name": "source", "in": "query", "required": false, "schema": { "type": "string", "enum": [ "bundle_social", "apify", "manual" ] } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 30, "default": 5 } }, { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date-time" } }, { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date-time" } } ], "responses": { "200": { "description": "Raw post analytics snapshots.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "403": { "description": "Plan does not include raw analytics payloads.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Post not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/analytics/posts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/raw?source=example&limit=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/comments": { "get": { "operationId": "listCommentTasks", "summary": "List comment tasks", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "page", "in": "query", "required": false, "description": "Page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }, { "name": "per_page", "in": "query", "required": false, "description": "Items per page.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } } ], "responses": { "200": { "description": "Paginated comment task list.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/comments?page=example&per_page=example\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments?page=example&per_page=example', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/comments?page=example&per_page=example',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/comments?page=example&per_page=example\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "post": { "operationId": "createCommentTasks", "summary": "Create comment tasks", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCommentTaskRequest" } } } }, "responses": { "201": { "description": "Created comment tasks.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid comment task request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "402": { "description": "Insufficient credits.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/comments\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"saved_account_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n \"target_video_url\": \"https://example.com/resource\",\n \"comment_text\": \"comment_text\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"saved_account_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n \"target_video_url\": \"https://example.com/resource\",\n \"comment_text\": \"comment_text\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/comments',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"saved_account_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n \"target_video_url\": \"https://example.com/resource\",\n \"comment_text\": \"comment_text\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/comments\", strings.NewReader(\"{\\n \\\"saved_account_id\\\": \\\"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\\\",\\n \\\"target_video_url\\\": \\\"https://example.com/resource\\\",\\n \\\"comment_text\\\": \\\"comment_text\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/comments/{id}": { "get": { "operationId": "getCommentTask", "summary": "Get a comment task", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Comment task ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Comment task.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Comment task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] }, "delete": { "operationId": "deleteCommentTask", "summary": "Delete a comment task", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Comment task ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Delete result.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Comment task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X DELETE \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {\n method: 'DELETE',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'DELETE',\n 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"DELETE\", \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/comments/{id}/approve": { "post": { "operationId": "approveCommentTask", "summary": "Approve a manually confirmed comment task", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Comment task ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Approved comment task.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Comment task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Comment task status cannot be approved.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/approve\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/comments/{id}/dispute": { "post": { "operationId": "disputeCommentTask", "summary": "Dispute a manually confirmed comment task", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Comment task ID.", "schema": { "type": "string", "format": "uuid" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DisputeCommentRequest" } } } }, "responses": { "200": { "description": "Disputed comment task.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "400": { "description": "Invalid dispute reason.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "404": { "description": "Comment task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } }, "409": { "description": "Comment task status cannot be disputed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } }, "Idempotent-Replayed": { "description": "Present with value true when a mutating request is replayed from a completed Idempotency-Key.", "schema": { "type": "string", "enum": [ "true" ] } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X POST \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute\" \\\n -H \"X-API-Key: sk_your_key_here\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"reason\": \"Please review this item.\"\n}'" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute', {\n method: 'POST',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"reason\": \"Please review this item.\"\n})\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'POST',\n 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]},\n json={\n \"reason\": \"Please review this item.\"\n }\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"POST\", \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/dispute\", strings.NewReader(\"{\\n \\\"reason\\\": \\\"Please review this item.\\\"\\n}\"))\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nreq.Header.Set(\"Content-Type\", \"application/json\")\nresp, err := http.DefaultClient.Do(req)" } ] } }, "/comments/{id}/verifications": { "get": { "operationId": "listCommentTaskVerifications", "summary": "List comment task verification events", "tags": [ "Comments" ], "security": [ { "ApiKeyAuth": [] } ], "parameters": [ { "name": "id", "in": "path", "required": true, "description": "Comment task ID.", "schema": { "type": "string", "format": "uuid" } } ], "responses": { "200": { "description": "Verification timeline.", "content": { "application/json": { "schema": { "type": "object" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } }, "404": { "description": "Comment task not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }, "headers": { "X-TokPortal-API-Version": { "description": "Current TokPortal public API contract version.", "schema": { "type": "string", "example": "2026-05-25" } }, "X-TokPortal-API-Stability": { "description": "Stability channel for the public API contract.", "schema": { "type": "string", "example": "stable" } }, "X-TokPortal-Request-ID": { "description": "Request correlation ID. Echoes a valid X-Request-ID request header or uses a generated req_ identifier.", "schema": { "type": "string", "example": "req_0123456789abcdef0123456789abcdef" } }, "X-RateLimit-Limit": { "description": "Maximum token bucket capacity for the authenticated API key.", "schema": { "type": "integer", "example": 120 } }, "X-RateLimit-Remaining": { "description": "Approximate requests remaining for the authenticated API key after this request.", "schema": { "type": "integer", "example": 119 } }, "X-RateLimit-Reset": { "description": "Unix timestamp when the token bucket is expected to be full again.", "schema": { "type": "integer", "example": 1779724800 } }, "Retry-After": { "description": "Seconds to wait before retrying after RATE_LIMIT_EXCEEDED. Present on 429 responses.", "schema": { "type": "integer", "example": 1 } } } } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", "source": "curl -X GET \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications\" \\\n -H \"X-API-Key: sk_your_key_here\"" }, { "lang": "JavaScript", "label": "Node", "source": "const response = await fetch('https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications', {\n method: 'GET',\n headers: {\n 'X-API-Key': process.env.TOKPORTAL_API_KEY!,\n }\n});\nconst data = await response.json();" }, { "lang": "Python", "label": "Python", "source": "import os\nimport requests\n\nresponse = requests.request(\n 'GET',\n 'https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications',\n headers={\"X-API-Key\": os.environ[\"TOKPORTAL_API_KEY\"]}\n)\nprint(response.json())" }, { "lang": "Go", "label": "Go", "source": "req, _ := http.NewRequest(\"GET\", \"https://app.tokportal.com/api/ext/comments/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verifications\", nil)\nreq.Header.Set(\"X-API-Key\", os.Getenv(\"TOKPORTAL_API_KEY\"))\nresp, err := http.DefaultClient.Do(req)" } ] } } }, "components": { "securitySchemes": { "ApiKeyAuth": { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "TokPortal API key. New keys use the format sk_ followed by 64 lowercase hex characters, are stored as SHA-256 hashes, are shown once at creation, and should never be sent in query parameters." } }, "schemas": { "ErrorResponse": { "type": "object", "required": [ "error" ], "properties": { "error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": { "type": "object", "additionalProperties": true } } } } }, "MeResponse": { "type": "object", "required": [ "data" ], "properties": { "data": { "type": "object", "required": [ "id", "email", "credits", "api_key" ], "properties": { "id": { "type": "string", "format": "uuid" }, "email": { "type": "string", "format": "email" }, "full_name": { "type": [ "string", "null" ] }, "company_name": { "type": [ "string", "null" ] }, "role": { "type": [ "string", "null" ] }, "country": { "type": [ "string", "null" ] }, "credits": { "type": "object", "required": [ "balance" ], "properties": { "balance": { "type": "number" } } }, "subscription": { "type": [ "object", "null" ], "additionalProperties": true }, "api_key": { "type": "object", "required": [ "id", "name" ], "properties": { "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" } } } } } } }, "CreateBundleRequest": { "type": "object", "required": [ "bundle_type" ], "properties": { "bundle_type": { "type": "string", "enum": [ "account_only", "account_and_videos", "videos_only" ] }, "platform": { "type": "string", "enum": [ "tiktok", "instagram" ], "default": "tiktok" }, "country": { "type": "string", "minLength": 2, "maxLength": 5, "description": "Required unless bundle_type is videos_only." }, "title": { "type": "string", "maxLength": 200 }, "videos_quantity": { "type": "integer", "minimum": 0, "maximum": 500, "default": 0 }, "edits_quantity": { "type": "integer", "minimum": 0, "maximum": 500, "default": 0 }, "wants_niche_warming": { "type": "boolean", "default": false }, "wants_deep_warming": { "type": "boolean", "default": false }, "wants_moderation": { "type": "boolean", "default": false }, "niche_warming_instructions": { "type": "string", "maxLength": 2000 }, "moderation_notice": { "type": "string", "maxLength": 2000 }, "auto_finalize_videos": { "type": "boolean", "default": true }, "account_id": { "type": "string", "format": "uuid", "description": "Required for videos_only bundles." }, "external_ref": { "type": "string", "maxLength": 200 } }, "additionalProperties": false }, "PatchBundleRequest": { "type": "object", "properties": { "auto_finalize_videos": { "type": "boolean" }, "external_ref": { "type": [ "string", "null" ], "maxLength": 200 }, "title": { "type": [ "string", "null" ], "maxLength": 200 } }, "minProperties": 1, "additionalProperties": false }, "CreateBundleResponse": { "type": "object", "properties": { "data": { "type": "object", "additionalProperties": true }, "credits_charged": { "type": "number" } } }, "CreateBulkBundlesRequest": { "type": "object", "required": [ "platforms", "country", "accounts_count" ], "properties": { "platforms": { "type": "array", "minItems": 1, "maxItems": 3, "items": { "type": "string", "enum": [ "tiktok", "instagram" ] } }, "country": { "type": "string", "minLength": 2, "maxLength": 5 }, "accounts_count": { "type": "integer", "minimum": 1, "maximum": 100 }, "upload_accounts_count": { "type": "integer", "minimum": 0, "maximum": 100, "default": 0 }, "videos_per_account": { "type": "integer", "minimum": 0, "maximum": 500, "default": 0 }, "wants_niche_warming": { "type": "boolean", "default": false }, "wants_deep_warming": { "type": "boolean", "default": false }, "wants_moderation": { "type": "boolean", "default": false }, "niche_warming_instructions": { "type": "string", "maxLength": 2000 }, "moderation_notice": { "type": "string", "maxLength": 2000 }, "auto_finalize_videos": { "type": "boolean", "default": true }, "external_ref": { "type": "string", "maxLength": 200 } }, "additionalProperties": false }, "ConfigureAccountRequest": { "type": "object", "required": [ "username", "visible_name" ], "properties": { "username": { "type": "string", "minLength": 1, "maxLength": 24, "pattern": "^[a-zA-Z0-9_.]+$", "description": "Requested username. Cannot end with a dot." }, "visible_name": { "type": "string", "minLength": 1, "maxLength": 30 }, "biography": { "type": "string", "maxLength": 80 }, "profile_picture_url": { "type": "string", "description": "Public image URL or TokPortal storage URL/path." }, "link_in_bio": { "type": "string", "format": "uri" }, "niche_warming_instructions": { "type": "string", "maxLength": 2000 } }, "additionalProperties": false }, "ConfigureVideoRequest": { "type": "object", "required": [ "video_type", "description", "target_publish_date" ], "properties": { "video_type": { "type": "string", "enum": [ "video", "carousel" ] }, "description": { "type": "string", "minLength": 1, "maxLength": 2200 }, "target_publish_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "YYYY-MM-DD." }, "name": { "type": "string", "maxLength": 200 }, "video_url": { "type": "string", "format": "uri" }, "carousel_images": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "string", "format": "uri" } }, "carousel_title": { "type": "string", "maxLength": 200 }, "tiktok_sound_url": { "type": "string", "format": "uri" }, "volume_original_sound": { "type": [ "integer", "null" ], "minimum": 0, "maximum": 200 }, "volume_added_sound": { "type": [ "integer", "null" ], "minimum": 0, "maximum": 200 }, "editing_instructions": { "type": "string", "maxLength": 5000 }, "external_ref": { "type": "string", "maxLength": 200 }, "instagram_content_type": { "type": "string", "enum": [ "reel", "post" ] }, "instagram_location": { "type": "string", "maxLength": 200 }, "instagram_collaborators": { "type": "array", "maxItems": 5, "items": { "type": "string" } }, "instagram_audio_name": { "type": "string", "maxLength": 200 }, "instagram_add_to_story": { "type": "boolean" }, "youtube_title": { "type": "string", "maxLength": 200 }, "youtube_tags": { "type": "array", "maxItems": 30, "items": { "type": "string" } }, "youtube_category": { "type": "string", "maxLength": 100 }, "youtube_visibility": { "type": "string", "enum": [ "public", "unlisted", "private" ] }, "youtube_sound_url": { "type": "string", "format": "uri" }, "auto_publish": { "type": "boolean", "default": false } }, "additionalProperties": false }, "PatchVideoRequest": { "type": "object", "properties": { "external_ref": { "type": [ "string", "null" ], "maxLength": 200 }, "name": { "type": [ "string", "null" ], "maxLength": 200 } }, "minProperties": 1, "additionalProperties": false }, "BatchConfigureVideosRequest": { "type": "object", "required": [ "videos" ], "properties": { "videos": { "type": "array", "minItems": 1, "maxItems": 500, "items": { "allOf": [ { "type": "object", "required": [ "video_type", "description", "target_publish_date" ], "properties": { "video_type": { "type": "string", "enum": [ "video", "carousel" ] }, "description": { "type": "string", "minLength": 1, "maxLength": 2200 }, "target_publish_date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "YYYY-MM-DD." }, "name": { "type": "string", "maxLength": 200 }, "video_url": { "type": "string", "format": "uri" }, "carousel_images": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "string", "format": "uri" } }, "carousel_title": { "type": "string", "maxLength": 200 }, "tiktok_sound_url": { "type": "string", "format": "uri" }, "volume_original_sound": { "type": [ "integer", "null" ], "minimum": 0, "maximum": 200 }, "volume_added_sound": { "type": [ "integer", "null" ], "minimum": 0, "maximum": 200 }, "editing_instructions": { "type": "string", "maxLength": 5000 }, "external_ref": { "type": "string", "maxLength": 200 }, "instagram_content_type": { "type": "string", "enum": [ "reel", "post" ] }, "instagram_location": { "type": "string", "maxLength": 200 }, "instagram_collaborators": { "type": "array", "maxItems": 5, "items": { "type": "string" } }, "instagram_audio_name": { "type": "string", "maxLength": 200 }, "instagram_add_to_story": { "type": "boolean" }, "youtube_title": { "type": "string", "maxLength": 200 }, "youtube_tags": { "type": "array", "maxItems": 30, "items": { "type": "string" } }, "youtube_category": { "type": "string", "maxLength": 100 }, "youtube_visibility": { "type": "string", "enum": [ "public", "unlisted", "private" ] }, "youtube_sound_url": { "type": "string", "format": "uri" }, "auto_publish": { "type": "boolean", "default": false } }, "additionalProperties": false }, { "type": "object", "required": [ "position" ], "properties": { "position": { "type": "integer", "minimum": 1 } } } ] } }, "auto_publish": { "type": "boolean", "default": false } }, "additionalProperties": false }, "QuantityRequest": { "type": "object", "required": [ "quantity" ], "properties": { "quantity": { "type": "integer", "minimum": 1, "maximum": 500 } }, "additionalProperties": false }, "AccountEditRequest": { "type": "object", "required": [ "requested_username", "requested_visible_name" ], "properties": { "requested_username": { "type": "string", "minLength": 1, "maxLength": 24 }, "requested_visible_name": { "type": "string", "minLength": 1, "maxLength": 30 }, "requested_biography": { "type": "string", "maxLength": 80 }, "requested_profile_picture_url": { "type": "string" }, "requested_link_in_bio": { "type": "string", "format": "uri" } }, "additionalProperties": false }, "AccountCorrectionsRequest": { "type": "object", "required": [ "comment" ], "properties": { "comment": { "type": "string", "minLength": 1, "maxLength": 2000 }, "fields": { "type": "object", "properties": { "username": { "type": "boolean" }, "visible_name": { "type": "boolean" }, "biography": { "type": "boolean" }, "profile_picture": { "type": "boolean" } }, "additionalProperties": false } }, "additionalProperties": false }, "VideoCorrectionsRequest": { "type": "object", "required": [ "comment" ], "properties": { "comment": { "type": "string", "minLength": 1, "maxLength": 2000 }, "fields": { "type": "object", "properties": { "video_content": { "type": "boolean" }, "description": { "type": "boolean" }, "video_editing": { "type": "boolean" }, "sound": { "type": "boolean" } }, "additionalProperties": false } }, "additionalProperties": false }, "FixVideoDownloadRequest": { "type": "object", "properties": { "video_url": { "type": "string", "format": "uri" }, "carousel_images": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": "string", "format": "uri" } } }, "anyOf": [ { "required": [ "video_url" ] }, { "required": [ "carousel_images" ] } ], "additionalProperties": false }, "RefreshAnalyticsRequest": { "type": "object", "properties": { "force": { "type": "boolean", "default": false }, "includePosts": { "type": "boolean", "default": true }, "includeComments": { "type": "boolean", "default": false }, "forcePosts": { "type": "boolean", "default": false }, "bootstrapPosts": { "type": "boolean", "default": false }, "postLimit": { "type": "integer", "minimum": 1, "maximum": 60, "default": 20 } }, "additionalProperties": false }, "CreateCommentTaskRequest": { "oneOf": [ { "type": "object", "required": [ "saved_account_id", "target_video_url", "comment_text" ], "properties": { "saved_account_id": { "type": "string", "format": "uuid" }, "target_video_url": { "type": "string", "minLength": 1 }, "comment_text": { "type": "string", "minLength": 1, "maxLength": 10000 }, "brief_id": { "type": [ "string", "null" ], "format": "uuid" } }, "additionalProperties": false }, { "type": "object", "required": [ "tasks" ], "properties": { "tasks": { "type": "array", "minItems": 1, "maxItems": 200, "items": { "type": "object", "required": [ "saved_account_id", "target_video_url", "comment_text" ], "properties": { "saved_account_id": { "type": "string", "format": "uuid" }, "target_video_url": { "type": "string", "minLength": 1 }, "comment_text": { "type": "string", "minLength": 1, "maxLength": 10000 }, "brief_id": { "type": [ "string", "null" ], "format": "uuid" } }, "additionalProperties": false } } }, "additionalProperties": false } ] }, "DisputeCommentRequest": { "type": "object", "required": [ "reason" ], "properties": { "reason": { "type": "string", "minLength": 3, "maxLength": 500 } }, "additionalProperties": false }, "CreateWebhookEndpointRequest": { "type": "object", "required": [ "url", "events" ], "properties": { "url": { "type": "string", "format": "uri", "maxLength": 2000 }, "events": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "enum": [ "webhook.test", "bundle.created", "bundle.published", "account.configured", "account.published", "account.in_review", "account.pending_corrections", "account.finalized", "video.configured", "video.in_review", "video.published", "video.pending_corrections", "video.finalized" ] } }, "description": { "type": "string", "maxLength": 500 }, "enabled": { "type": "boolean", "default": true } }, "additionalProperties": false }, "UpdateWebhookEndpointRequest": { "type": "object", "properties": { "url": { "type": "string", "format": "uri", "maxLength": 2000 }, "events": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "enum": [ "webhook.test", "bundle.created", "bundle.published", "account.configured", "account.published", "account.in_review", "account.pending_corrections", "account.finalized", "video.configured", "video.in_review", "video.published", "video.pending_corrections", "video.finalized" ] } }, "description": { "type": [ "string", "null" ], "maxLength": 500 }, "enabled": { "type": "boolean" } }, "minProperties": 1, "additionalProperties": false }, "WebhookEventsResponse": { "type": "object", "required": [ "data" ], "properties": { "data": { "type": "object", "required": [ "events", "envelope", "delivery", "signature" ], "properties": { "events": { "type": "array", "items": { "type": "object", "required": [ "type", "category", "availability", "description", "when", "payload_schema", "example_payload" ], "properties": { "type": { "type": "string", "enum": [ "webhook.test", "bundle.created", "bundle.published", "account.configured", "account.published", "account.in_review", "account.pending_corrections", "account.finalized", "video.configured", "video.in_review", "video.published", "video.pending_corrections", "video.finalized" ] }, "category": { "type": "string", "enum": [ "webhook", "bundle", "account", "video", "comment" ] }, "availability": { "type": "string", "enum": [ "emitted", "subscribable" ] }, "description": { "type": "string" }, "when": { "type": "string" }, "payload_schema": { "type": "object", "additionalProperties": true }, "example_payload": { "type": "object", "additionalProperties": true } } } }, "envelope": { "type": "object", "required": [ "id", "type", "api_version", "created_at", "data" ], "properties": { "id": { "type": "string", "pattern": "^evt_" }, "type": { "type": "string", "enum": [ "webhook.test", "bundle.created", "bundle.published", "account.configured", "account.published", "account.in_review", "account.pending_corrections", "account.finalized", "video.configured", "video.in_review", "video.published", "video.pending_corrections", "video.finalized" ] }, "api_version": { "type": "string", "example": "2026-05-25" }, "created_at": { "type": "string", "format": "date-time" }, "data": { "type": "object", "additionalProperties": true } } }, "delivery": { "type": "object", "required": [ "method", "content_type", "timeout_ms", "user_agent", "headers" ], "properties": { "method": { "type": "string", "enum": [ "POST" ] }, "content_type": { "type": "string", "enum": [ "application/json" ] }, "timeout_ms": { "type": "integer", "example": 8000 }, "user_agent": { "type": "string", "example": "TokPortal-Webhooks/1.0" }, "headers": { "type": "array", "items": { "type": "string" } } } }, "signature": { "type": "object", "required": [ "header", "scheme", "signed_payload", "algorithm", "default_tolerance_seconds" ], "properties": { "header": { "type": "string", "enum": [ "TokPortal-Signature" ] }, "scheme": { "type": "string", "example": "t={unix_timestamp},v1={hmac_sha256}" }, "signed_payload": { "type": "string", "example": "{timestamp}.{raw_body}" }, "algorithm": { "type": "string", "enum": [ "HMAC-SHA256" ] }, "default_tolerance_seconds": { "type": "integer", "example": 300 } } } }, "example": { "events": [ { "type": "webhook.test", "category": "webhook", "availability": "emitted", "description": "A signed test event sent on demand to validate an endpoint.", "when": "Sent when you call POST /webhooks/{id}/test.", "payload_schema": { "type": "object", "required": [ "webhook_endpoint_id", "message" ], "properties": { "webhook_endpoint_id": { "type": "string", "format": "uuid" }, "message": { "type": "string" } } }, "example_payload": { "webhook_endpoint_id": "33333333-4444-4555-8666-777777777777", "message": "TokPortal webhook test event" } }, { "type": "bundle.created", "category": "bundle", "availability": "emitted", "description": "A bundle was created through the public API.", "when": "Emitted after a successful bundle creation.", "payload_schema": { "type": "object", "required": [ "bundle_id" ], "properties": { "bundle_id": { "type": "string", "format": "uuid" }, "external_ref": { "type": [ "string", "null" ] }, "status": { "type": "string" }, "platform": { "type": "string" }, "bundle_type": { "type": "string" } } }, "example_payload": { "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "external_ref": "partner-order-123", "status": "pending_setup", "platform": "tiktok", "bundle_type": "account_and_videos" } }, { "type": "bundle.published", "category": "bundle", "availability": "emitted", "description": "A bundle was published.", "when": "Emitted after a bundle publish request succeeds.", "payload_schema": { "type": "object", "required": [ "bundle_id" ], "properties": { "bundle_id": { "type": "string", "format": "uuid" }, "external_ref": { "type": [ "string", "null" ] }, "status": { "type": "string" }, "platform": { "type": "string" }, "bundle_type": { "type": "string" } } }, "example_payload": { "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "external_ref": "partner-order-123", "status": "published", "platform": "tiktok", "bundle_type": "account_and_videos" } }, { "type": "account.configured", "category": "account", "availability": "emitted", "description": "An account listing was configured.", "when": "Emitted when PUT /bundles/{id}/account moves an account listing to configured.", "payload_schema": { "type": "object", "required": [ "account_id", "bundle_id", "status" ], "properties": { "account_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "username": { "type": [ "string", "null" ] }, "platform": { "type": "string" }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] } } }, "example_payload": { "account_id": "0d1e2f3a-4b5c-6789-8abc-def012345678", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "username": "launchprofile", "platform": "tiktok", "status": "configured", "previous_status": "pending" } }, { "type": "account.in_review", "category": "account", "availability": "emitted", "description": "An account listing entered customer review.", "when": "Emitted when a Bundle Social account handoff moves an accepted account listing to in_review.", "payload_schema": { "type": "object", "required": [ "account_id", "bundle_id", "status" ], "properties": { "account_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "username": { "type": [ "string", "null" ] }, "platform": { "type": "string" }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] } } }, "example_payload": { "account_id": "0d1e2f3a-4b5c-6789-8abc-def012345678", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "username": "launchprofile", "platform": "tiktok", "status": "in_review", "previous_status": "accepted" } }, { "type": "account.published", "category": "account", "availability": "emitted", "description": "An account listing was published to an active bundle.", "when": "Emitted when POST /bundles/{id}/publish moves a configured account listing to published.", "payload_schema": { "type": "object", "required": [ "account_id", "bundle_id", "status" ], "properties": { "account_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "username": { "type": [ "string", "null" ] }, "platform": { "type": "string" }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] } } }, "example_payload": { "account_id": "0d1e2f3a-4b5c-6789-8abc-def012345678", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "username": "launchprofile", "platform": "tiktok", "status": "published", "previous_status": "configured" } }, { "type": "account.pending_corrections", "category": "account", "availability": "emitted", "description": "Corrections were requested for an account listing.", "when": "Emitted after POST /bundles/{id}/account/corrections moves an account listing from in_review to pending_corrections.", "payload_schema": { "type": "object", "required": [ "account_id", "bundle_id", "status" ], "properties": { "account_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "username": { "type": [ "string", "null" ] }, "platform": { "type": "string" }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] } } }, "example_payload": { "account_id": "0d1e2f3a-4b5c-6789-8abc-def012345678", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "username": "launchprofile", "platform": "tiktok", "status": "pending_corrections", "previous_status": "in_review" } }, { "type": "account.finalized", "category": "account", "availability": "emitted", "description": "An account listing was approved and finalized.", "when": "Emitted after POST /bundles/{id}/account/finalize succeeds.", "payload_schema": { "type": "object", "required": [ "account_id", "bundle_id", "status" ], "properties": { "account_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "username": { "type": [ "string", "null" ] }, "platform": { "type": "string" }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] } } }, "example_payload": { "account_id": "0d1e2f3a-4b5c-6789-8abc-def012345678", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "username": "launchprofile", "platform": "tiktok", "status": "finalized", "previous_status": "in_review" } }, { "type": "video.configured", "category": "video", "availability": "emitted", "description": "A video listing was configured or unscheduled back to configured.", "when": "Emitted when a video configuration, CSV import, batch configuration, or unschedule action moves a video listing to configured.", "payload_schema": { "type": "object", "required": [ "video_id", "bundle_id", "position", "status" ], "properties": { "video_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] }, "platform_url": { "type": [ "string", "null" ], "format": "uri" } } }, "example_payload": { "video_id": "11111111-2222-4333-8444-555555555555", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "position": 1, "status": "configured", "previous_status": "pending", "platform_url": null } }, { "type": "video.in_review", "category": "video", "availability": "emitted", "description": "A video listing entered customer review.", "when": "Emitted when a manager submits a posted video URL and the video listing moves to in_review.", "payload_schema": { "type": "object", "required": [ "video_id", "bundle_id", "position", "status" ], "properties": { "video_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] }, "platform_url": { "type": [ "string", "null" ], "format": "uri" } } }, "example_payload": { "video_id": "11111111-2222-4333-8444-555555555555", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "position": 1, "status": "in_review", "previous_status": "configured", "platform_url": null } }, { "type": "video.published", "category": "video", "availability": "emitted", "description": "A video was published to its destination platform.", "when": "Emitted after POST /bundles/{id}/videos/{position}/publish succeeds.", "payload_schema": { "type": "object", "required": [ "video_id", "bundle_id", "position", "status" ], "properties": { "video_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] }, "platform_url": { "type": [ "string", "null" ], "format": "uri" } } }, "example_payload": { "video_id": "11111111-2222-4333-8444-555555555555", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "position": 1, "status": "published", "previous_status": "in_review", "platform_url": "https://www.tiktok.com/@launchprofile/video/123" } }, { "type": "video.pending_corrections", "category": "video", "availability": "emitted", "description": "Corrections were requested for a video listing.", "when": "Emitted after POST /bundles/{id}/videos/{position}/corrections moves a video listing from in_review to pending_corrections.", "payload_schema": { "type": "object", "required": [ "video_id", "bundle_id", "position", "status" ], "properties": { "video_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] }, "platform_url": { "type": [ "string", "null" ], "format": "uri" } } }, "example_payload": { "video_id": "11111111-2222-4333-8444-555555555555", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "position": 1, "status": "pending_corrections", "previous_status": "in_review", "platform_url": "https://www.tiktok.com/@launchprofile/video/123" } }, { "type": "video.finalized", "category": "video", "availability": "emitted", "description": "A video passed final review.", "when": "Emitted after POST /bundles/{id}/videos/{position}/finalize succeeds.", "payload_schema": { "type": "object", "required": [ "video_id", "bundle_id", "position", "status" ], "properties": { "video_id": { "type": "string", "format": "uuid" }, "bundle_id": { "type": "string", "format": "uuid" }, "position": { "type": "integer", "minimum": 1 }, "status": { "type": "string" }, "previous_status": { "type": [ "string", "null" ] }, "platform_url": { "type": [ "string", "null" ], "format": "uri" } } }, "example_payload": { "video_id": "11111111-2222-4333-8444-555555555555", "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", "position": 1, "status": "finalized", "previous_status": "in_review", "platform_url": "https://www.tiktok.com/@launchprofile/video/123" } } ], "envelope": { "id": "evt_0123456789abcdef0123456789abcdef", "type": "bundle.published", "api_version": "2026-05-25", "created_at": "2026-06-01T12:00:00.000Z", "data": {} }, "delivery": { "method": "POST", "content_type": "application/json", "timeout_ms": 8000, "user_agent": "TokPortal-Webhooks/1.0", "headers": [ "TokPortal-Event-Id", "TokPortal-Event-Type", "TokPortal-Signature" ] }, "signature": { "header": "TokPortal-Signature", "scheme": "t={unix_timestamp},v1={hmac_sha256}", "signed_payload": "{timestamp}.{raw_body}", "algorithm": "HMAC-SHA256", "default_tolerance_seconds": 300 } } } } }, "UploadVideoRequest": { "type": "object", "required": [ "filename", "bundle_id" ], "properties": { "filename": { "type": "string", "minLength": 1, "maxLength": 200 }, "content_type": { "type": "string", "default": "video/mp4" }, "bundle_id": { "type": "string", "format": "uuid" } }, "additionalProperties": false }, "UploadImageRequest": { "type": "object", "required": [ "filename", "content_type", "bundle_id" ], "properties": { "filename": { "type": "string", "minLength": 1, "maxLength": 200 }, "content_type": { "type": "string", "pattern": "^image/(jpeg|jpg|png|webp|gif)$", "description": "Allowed image MIME types: image/jpeg, image/jpg, image/png, image/webp, image/gif." }, "bundle_id": { "type": "string", "format": "uuid" }, "purpose": { "type": "string", "enum": [ "carousel", "profile_picture" ], "default": "carousel" } }, "additionalProperties": false }, "UploadImageFromUrlRequest": { "type": "object", "required": [ "url", "bundle_id" ], "properties": { "url": { "type": "string", "format": "uri", "description": "Public direct image URL." }, "bundle_id": { "type": "string", "format": "uuid" }, "purpose": { "type": "string", "enum": [ "carousel", "profile_picture" ], "default": "carousel" } }, "additionalProperties": false }, "CreateAnalyticsReportRequest": { "type": "object", "properties": { "title": { "type": [ "string", "null" ], "maxLength": 200 }, "template": { "type": [ "string", "null" ], "maxLength": 100 }, "brandName": { "type": [ "string", "null" ], "maxLength": 100 }, "brandAccent": { "type": [ "string", "null" ], "maxLength": 40 }, "accountIds": { "type": "array", "items": { "type": "string", "format": "uuid" } }, "workspaceId": { "type": [ "string", "null" ], "format": "uuid" }, "platforms": { "type": "array", "items": { "type": "string", "enum": [ "tiktok", "instagram" ] } }, "countries": { "type": "array", "items": { "type": "string", "minLength": 2, "maxLength": 5 } }, "query": { "type": [ "string", "null" ], "maxLength": 200 }, "from": { "type": [ "string", "null" ], "format": "date" }, "to": { "type": [ "string", "null" ], "format": "date" } }, "additionalProperties": false } } }, "x-tokportal": { "schemaStatus": "partial-foundation", "canonicalBaseUrl": "https://app.tokportal.com/api/ext", "intendedConsumers": [ "docs", "typescript-sdk", "python-sdk", "go-sdk", "cli", "mcp" ] } } ```