Bundles

Create Bundle - Provision Accounts & Videos

Create a new TokPortal bundle via API to provision accounts and video slots for TikTok or Instagram campaigns.

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

FieldTypeDescription
bundle_typestring"account_only", "account_and_videos", or "videos_only".

Conditional

FieldTypeRequired WhenDescription
countrystringaccount_only, account_and_videosTokPortal country code or supported alias. Use GET /countries to list enabled values. Optional for videos_only (derived from the saved account).
account_idstring (UUID)videos_onlyThe 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_quantityintegeraccount_and_videos, videos_onlyNumber of video slots. Must be greater than 0.

Optional

FieldTypeDefaultDescription
platformstring"tiktok""tiktok" or "instagram".
titlestringBundle title. Max 200 characters.
edits_quantityinteger0Number of video edits. Cannot exceed videos_quantity.
wants_niche_warmingbooleanfalseEnable niche warming. Mutually exclusive with wants_deep_warming.
wants_deep_warmingbooleanfalseEnable deep warming. Instagram only. Mutually exclusive with wants_niche_warming.
wants_moderationbooleanfalseEnable content moderation.
niche_warming_instructionsstringInstructions for niche warming. Required when wants_niche_warming is true.
moderation_noticestringOptional notice for the moderation team.
auto_finalize_videosbooleantrueAutomatically finalize videos when they are ready.
external_refstringYour 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

{
  "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
  }
}
FieldTypeDescription
data.bundle_idstringUnique identifier for the created bundle.
data.bundle_typestringThe bundle type as submitted.
data.platformstringTarget platform.
data.countrystringCountry code.
data.statusstringInitial bundle status.
data.videos_quantityintegerNumber of video slots (if applicable).
data.edits_quantityintegerNumber of edits (if applicable).
data.external_refstringYour external reference (if provided).
data.created_atstringISO 8601 timestamp.
credits_chargedintegerCredits deducted for this bundle.
credits_remainingintegerYour remaining credit balance.
cost_breakdownobjectItemized cost breakdown.

Examples

1. Account and Videos (with niche warming)

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:

{
  "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

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:

{
  "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 endpoint on an existing active bundle. This preserves the same account manager, which is better for continuity and turnaround time.

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:

{
  "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

StatusCodeDescription
400VALIDATION_ERRORInvalid or missing fields (see details for specifics).
400WARMING_CONFLICTwants_niche_warming and wants_deep_warming cannot both be enabled.
400DEEP_WARMING_INSTAGRAM_ONLYwants_deep_warming is only available on Instagram.
400EDITS_EXCEED_VIDEOSedits_quantity exceeds videos_quantity.
400COUNTRY_NOT_ENABLEDCountry is not enabled for your organization.
400ACCOUNT_ID_NOT_ALLOWEDaccount_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.
402INSUFFICIENT_CREDITSNot enough credits to create this bundle.
404ACCOUNT_NOT_FOUNDaccount_id does not exist or does not belong to your organization.