For your AI
Account Configuration

Account Configuration

Configure TikTok and Instagram account details via the TokPortal API. Set username, display name, bio, and profile picture.

Markdown

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

FieldTypeRequiredDescription
usernamestringYesAccount username. 1-24 characters, alphanumeric plus . and _.
visible_namestringYesDisplay name. 1-30 characters.
biographystringYesAccount biography. Max 80 characters.
profile_picture_urlstringYesURL to the profile picture image.
link_in_biostringNoLink displayed in the bio. Instagram bundles only. On TikTok and every other platform it is still accepted, stored as null, and reported back in the response's _warnings array — read _warnings, not just the 200.
niche_warming_instructionsstringNo⚠️ Deprecated. Legacy — kept only for accounts being re-created after a refund; use Advanced Niche Warming (wants_advanced_warming).
advanced_warming_termsstring[]NoBackward-compatible way to set targets while the profile is editable. New integrations should use PUT /bundles/{id}/warming-terms, which works at any account status. Must sanitize to exactly the purchased count and can only be set once.

Validation Rules

FieldRule
username1-24 characters. Allowed: [a-zA-Z0-9_.]
visible_name1-30 characters.
biographyMax 80 characters.
profile_picture_urlMust be a valid URL.
link_in_bioMust be a valid URL. Stored on Instagram bundles only; accepted but nulled elsewhere, with a _warnings entry.

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).

WARNING: This write is a full replacement PUT /bundles/:id/account replaces the whole profile. Any field you leave out is written back as null, so re-send the complete profile even when you only mean to change one part of it.

Unrecognised fields are refused, not dropped

Because the write is a full replace, an unrecognised field name used to be quietly stripped by validation — and the value it was meant to set was wiped to null. A single typo (biograhpy instead of biography) therefore erased the stored biography and still returned 200.

That no longer happens. A body carrying a field TokPortal does not recognise is rejected with 400 UNKNOWN_FIELD, carrying details.unknown_fields, a details.did_you_mean suggestion, and details.accepted_fields.

The same rule applies to the other full-replace writes: POST /bundles, POST /bundles/bulk, PUT /bundles/:id/account, PUT /bundles/:id/videos/:position, PUT /bundles/:id/videos/batch and PUT /bundles/:id/warming-terms. On every other endpoint nothing can be lost, so the request runs normally and the unrecognised names come back in the response's meta.ignored_fields.

Which callers get the 400 is controlled by the server-side API_STRICT_UNKNOWN_FIELDS setting:

ValueBehaviour
mcp (default)MCP callers are refused on full-replace endpoints; every other client keeps its 200 and reads meta.ignored_fields.
allEvery caller is refused on full-replace endpoints.
offNo caller is ever refused; unrecognised names are always reported.

Under the default, REST integrations see meta.ignored_fields today rather than a failure — treat it as the same warning and fix the field name.

Advanced Niche Warming targets

If the bundle was created with wants_advanced_warming: true and a target count (advanced_warming_terms_count), configure the targets with PUT /bundles/{id}/warming-terms. This account endpoint keeps accepting the same field for backward compatibility only while the account profile remains editable.

  • Pass advanced_warming_terms with exactly the purchased number of targets, all distinct, each 2-50 characters. Entries are trimmed and compared case-insensitively; anything dropped as a duplicate or for its length shrinks the list, and the call then fails with 400 ADVANCED_WARMING_TERMS and details.purchased_terms_count rather than configuring fewer targets than you paid for.
  • Configuration is one-shot per warming purchase — afterwards the API returns 409 WARMING_TERMS_ALREADY_SET.
  • Warming starts immediately when the account/order is already active (the warming.session_started webhook fires); otherwise it starts automatically at account submission / order accept.
  • Count-only purchases never configured within 14 days are auto-cancelled and fully refunded.
  • On success the response includes "_warming": { "terms_configured": true, "started": <bool> }, and the account's advanced_warming summary reflects the new state (terms_configured: true).

Example

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"
  }'

Response

{
  "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": null,
    "advanced_warming": null,
    "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.

advanced_warming summarizes the latest Advanced Niche Warming session of the account (session_id, status, terms_total, terms_verified, started_at, completed_at) or is null when the account has none. Fetch the full per-term reports with GET /warming-sessions/{session_id}. The is_niche_warmed, is_deep_warmed, deep_warming_verification_status and niche_warming_instructions fields are legacy — kept only for accounts being re-created after a refund; use Advanced Niche Warming (wants_advanced_warming).

If an external profile_picture_url was provided, it is automatically downloaded and re-hosted; the response will include _info.profile_picture_rehosted: true.

WARNING: Always read _warnings A 200 does not mean every field you sent was stored. When TokPortal accepts a request but could not apply part of it, it returns a _warnings array of plain-language strings alongside data. The two cases to expect here:

  • link_in_bio sent on a bundle that is not Instagram — accepted, stored as null, echoed back as null, and reported in _warnings.
  • A missing profile picture.
{
  "data": {
    "id": "acc_xyz789",
    "platform": "tiktok",
    "link_in_bio": null
  },
  "_warnings": [
    "link_in_bio was not stored: this is a tiktok bundle and a link in bio is an Instagram-only profile field. The response echoes link_in_bio: null."
  ]
}
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

{
  "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,
    "advanced_warming": {
      "session_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "status": "in_progress",
      "terms_total": 6,
      "terms_verified": 2,
      "started_at": "2026-07-10T12:00:00Z",
      "completed_at": 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

curl -X GET https://app.tokportal.com/api/ext/bundles/bnd_abc123/account \
  -H "X-API-Key: sk_xxx"

Response

{
  "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": null,
    "feedback": null,
    "created_at": "2026-02-10T10:00:00Z"
  }
}

Error Responses

StatusCodeDescription
400invalid_usernameUsername does not match allowed pattern [a-zA-Z0-9_.] or exceeds 24 characters.
400invalid_visible_nameVisible name is empty or exceeds 30 characters.
400biography_too_longBiography exceeds 80 characters.
400invalid_urlprofile_picture_url or link_in_bio is not a valid URL.
400UNKNOWN_FIELDThe body carries a field TokPortal does not recognise. This write is a full replace, so the name is refused rather than dropped — see Unrecognised fields are refused, not dropped.
400ADVANCED_WARMING_TERMSadvanced_warming_terms does not sanitize to exactly the purchased target count (details.purchased_terms_count).
409invalid_statusBundle is not in pending_setup or configured status.
409DUPLICATE_ACCOUNT_BUNDLEAn 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.
409WARMING_TERMS_ALREADY_SETThe Advanced Niche Warming targets are already configured (or the session already started). Targets can only be set once per warming purchase.
404bundle_not_foundBundle does not exist.
404WARMING_SESSION_NOT_FOUNDadvanced_warming_terms was passed but the bundle has no warming session to configure (e.g. created without Advanced Niche Warming).