Source: https://developers.tokportal.com/api-reference/
Markdown: https://developers.tokportal.com/api-reference.md

# API Reference

This page is generated from the public OpenAPI schema. Do not edit it by hand.

Generated operations: **154**.

- [Download OpenAPI JSON](https://developers.tokportal.com/openapi.json)
- [Download OpenAPI YAML](https://developers.tokportal.com/openapi.yaml)

## Accounts

### Reply to an Instagram direct message

```http
POST /accounts/{id}/inbox/messages
```

Operation ID: `sendAccountInboxMessage`

Send plain text in an existing owned Instagram conversation, on an eligible Creator or Business connection. No Facebook Page. Costs 0 credits for this release. Supply the conversation reference from GET inbox kind=conversations. Requires write access, current Coverage, live messaging permissions and an incoming message in the past 24 hours; no cold outreach. The server verifies the latest 100 messages and refuses without positive window proof. Keep request\_key unchanged for an exact retry. Shared account leases and provider quotas apply. pending/unknown is not success and must not be resent; inspect GET operation and thread. succeeded means the provider returned a message ID, not recipient delivery or reading. Dry-run validates without sending.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (InboxMessageInput)

| Status | Description                                             |
| -----: | ------------------------------------------------------- |
|  `200` | Scoped result.                                          |
|  `400` | Invalid input or expired reference.                     |
|  `401` | Authentication required.                                |
|  `403` | Write access required.                                  |
|  `404` | Owned resource not found.                               |
|  `409` | Connection, Coverage, reply window or version conflict. |
|  `429` | Account cooldown; honor Retry-After.                    |
|  `503` | Unavailable or unconfirmed. Never blindly resend.       |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/messages" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "conversation_reference": "conversation_reference"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/messages', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "conversation_reference": "conversation_reference"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/messages',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"text\": \"text\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"conversation_reference\": \"conversation_reference\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/messages", strings.NewReader("{\n  \"text\": \"text\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"conversation_reference\": \"conversation_reference\"\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)
```

### Send a comment-to-DM private reply

```http
POST /accounts/{id}/inbox/private-replies
```

Operation ID: `createAccountPrivateReply`

Send one plain-text private Instagram reply to a new received comment on an owned post, within seven days. Creator and Business are supported with comment access. Costs 0 credits. Pass the post reference and comment private\_reply\_action\_reference. No TikTok DMs. Hidden, own, stale or unverifiable comments are refused. This consumes the single private reply for the comment; another key or changed wording cannot resend it. A platform refusal may consume it too. The recipient must send an incoming message before follow-up DMs; a private reply does not open a 24-hour window. Use a stable request\_key, honor Retry-After and never retry unknown delivery blindly. Dry-run validates without sending.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (PrivateReplyInput)

| Status | Description                                             |
| -----: | ------------------------------------------------------- |
|  `200` | Scoped result.                                          |
|  `400` | Invalid input or expired reference.                     |
|  `401` | Authentication required.                                |
|  `403` | Write access required.                                  |
|  `404` | Owned resource not found.                               |
|  `409` | Connection, Coverage, reply window or version conflict. |
|  `429` | Account cooldown; honor Retry-After.                    |
|  `503` | Unavailable or unconfirmed. Never blindly resend.       |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/private-replies" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "post_reference": "post_reference",
  "comment_reference": "comment_reference"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/private-replies', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "post_reference": "post_reference",
  "comment_reference": "comment_reference"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/private-replies',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"text\": \"text\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"post_reference\": \"post_reference\",\n  \"comment_reference\": \"comment_reference\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/private-replies", strings.NewReader("{\n  \"text\": \"text\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"post_reference\": \"post_reference\",\n  \"comment_reference\": \"comment_reference\"\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)
```

### Read a DM or private-reply result

```http
GET /accounts/{id}/inbox/operations/{operationId}
```

Operation ID: `getAccountMessageOperation`

Read the durable, owner-scoped operation state. A stale pending operation becomes unknown after two minutes, with no resend. Native message content and provider credentials are not returned.

| Parameter     |   In | Required | Description       |
| ------------- | ---: | -------: | ----------------- |
| `id`          | path |      Yes | Saved account ID. |
| `operationId` | path |      Yes |                   |

| Status | Description                                             |
| -----: | ------------------------------------------------------- |
|  `200` | Scoped result.                                          |
|  `400` | Invalid input or expired reference.                     |
|  `401` | Authentication required.                                |
|  `403` | Write access required.                                  |
|  `404` | Owned resource not found.                               |
|  `409` | Connection, Coverage, reply window or version conflict. |
|  `429` | Account cooldown; honor Retry-After.                    |
|  `503` | Unavailable or unconfirmed. Never blindly resend.       |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/operations/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/inbox/operations/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/operations/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/inbox/operations/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read comment-to-DM rules and history

```http
GET /accounts/{id}/inbox/comment-to-dm
```

Operation ID: `getAccountCommentDm`

Read one account rule and the latest 20 job results. Instagram only. Disabled by default. GET settings.version before editing. The optional post\_reference is freshly signed for the saved post scope. next\_cursor reads older history. No comment or DM bodies are retained in job history; the owner-configured message template is returned in settings.

| Parameter |    In | Required | Description       |
| --------- | ----: | -------: | ----------------- |
| `id`      |  path |      Yes | Saved account ID. |
| `cursor`  | query |       No |                   |

| Status | Description                                             |
| -----: | ------------------------------------------------------- |
|  `200` | Scoped result.                                          |
|  `400` | Invalid input or expired reference.                     |
|  `401` | Authentication required.                                |
|  `403` | Write access required.                                  |
|  `404` | Owned resource not found.                               |
|  `409` | Connection, Coverage, reply window or version conflict. |
|  `429` | Account cooldown; honor Retry-After.                    |
|  `503` | Unavailable or unconfirmed. Never blindly resend.       |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm?cursor=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/inbox/comment-to-dm?cursor=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm?cursor=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/inbox/comment-to-dm?cursor=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Configure keyword comment-to-DM

```http
PUT /accounts/{id}/inbox/comment-to-dm
```

Operation ID: `updateAccountCommentDm`

Explicitly enable, edit or pause one Instagram keyword rule, account-wide (post\_reference=null) or for a signed owned post reference. Costs 0 credits. Whole-word/contains and exceptions use the same Unicode matching as moderation. Rules apply only to original comments created after saving; no historical scan, own-account triggers or reply loops. Defaults disabled. Requires version for optimistic concurrency. Rule changes skip queued old-version jobs. Current owner, connection pin, Coverage, comment age/text and rules are rechecked before sending. Durable resource dedupe and a shared per-account lease prevent duplicate sends. Provider webhooks trigger jobs, normally immediately; a five-minute worker recovers queued work. At most three safe verification or explicit-quota retries. An ambiguous write is never automatically retried. A private reply never authorizes follow-up DMs until the person replies. Read history and subscribe to inbox.comment.automation.updated. Dry-run validates without saving or sending.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CommentDmInput)

| Status | Description                                             |
| -----: | ------------------------------------------------------- |
|  `200` | Scoped result.                                          |
|  `400` | Invalid input or expired reference.                     |
|  `401` | Authentication required.                                |
|  `403` | Write access required.                                  |
|  `404` | Owned resource not found.                               |
|  `409` | Connection, Coverage, reply window or version conflict. |
|  `429` | Account cooldown; honor Retry-After.                    |
|  `503` | Unavailable or unconfirmed. Never blindly resend.       |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X PUT "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false,
  "keywords": [
    "keywords"
  ],
  "exceptions": [
    "exceptions"
  ],
  "match_mode": "word",
  "message": "message",
  "post_reference": "post_reference",
  "version": 25
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "enabled": false,
  "keywords": [
    "keywords"
  ],
  "exceptions": [
    "exceptions"
  ],
  "match_mode": "word",
  "message": "message",
  "post_reference": "post_reference",
  "version": 25
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PUT',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"enabled\": false,\n  \"keywords\": [\n    \"keywords\"\n  ],\n  \"exceptions\": [\n    \"exceptions\"\n  ],\n  \"match_mode\": \"word\",\n  \"message\": \"message\",\n  \"post_reference\": \"post_reference\",\n  \"version\": 25\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-to-dm", strings.NewReader("{\n  \"enabled\": false,\n  \"keywords\": [\n    \"keywords\"\n  ],\n  \"exceptions\": [\n    \"exceptions\"\n  ],\n  \"match_mode\": \"word\",\n  \"message\": \"message\",\n  \"post_reference\": \"post_reference\",\n  \"version\": 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 TokPortal Coverage status

```http
GET /accounts/{id}/managed-subscription
```

Operation ID: `getAccountManagedSubscription`

Returns the effective TokPortal Coverage status, current 30-day period, account-specific rate, unpaid periods, task access, and an exact timestamped reactivation quote for one eligible saved account. At current\_period\_end, status and task\_access change immediately even if recorded\_status remains included or active for a few minutes until the renewal worker persists the transition; period\_expired makes that boundary explicit. A zero-credit quote is valid. Use its current\_period\_end and lock\_version snapshot for reactivation. An eligible TikTok or Instagram account created before the global Coverage cutoff is grandfathered and has no subscription record. A missing record can also mean the account is not Coverage-eligible or has not reached delivery, so clients must not infer grandfathering from a generic 404. Coverage is independent from bundle completion or closure.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | TokPortal Coverage status.            |
|  `401` | Missing, invalid, or revoked API key. |
|  `404` | Account or coverage record 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription" \
  -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/managed-subscription', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription',
    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/managed-subscription", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Reactivate TokPortal Coverage

```http
POST /accounts/{id}/managed-subscription/reactivate
```

Operation ID: `reactivateAccountManagedSubscription`

Reactivates TokPortal Coverage from an explicit GET snapshot, and debits the projected unpaid 30-day periods — read the quote from getAccountManagedSubscription and confirm the amount with the user first. Reactivation is free while the current billing period is already paid or included. After cancellation, access continues through the current paid period if no overdue Coverage is unpaid. After it expires, account features stay unavailable until Coverage is restored. At and after current\_period\_end, the endpoint accepts the effective lapsed state even if recorded\_status has not yet been updated by the renewal worker, and charges exactly the projected unpaid periods without adding an extra period. The expected credits, period end, and lock version are checked atomically before any debit or task resume. Scheduled videos receive new future dates while preserving their cadence.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (ManagedAccountSubscriptionReactivationRequest)

| Status | Description                                                                                                                      |
| -----: | -------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Coverage reactivated and withheld work resumed.                                                                                  |
|  `401` | Missing, invalid, or revoked API key.                                                                                            |
|  `402` | Insufficient credits. The response details include the exact required amount.                                                    |
|  `404` | Account or coverage record not found.                                                                                            |
|  `409` | Coverage is not paused, the account is unavailable, or the quote changed. Fetch a fresh snapshot before retrying QUOTE\_CHANGED. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/reactivate" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Idempotency-Key: reactivateAccountManagedSubscription-example-1" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_credits": 25,
  "expected_current_period_end": "2026-06-01T00:00:00Z",
  "expected_lock_version": 25
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/reactivate', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'reactivateAccountManagedSubscription-example-1',
  },
  body: JSON.stringify({
  "expected_credits": 25,
  "expected_current_period_end": "2026-06-01T00:00:00Z",
  "expected_lock_version": 25
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/reactivate',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"], "Idempotency-Key": "reactivateAccountManagedSubscription-example-1"},
    json=json.loads("{\n  \"expected_credits\": 25,\n  \"expected_current_period_end\": \"2026-06-01T00:00:00Z\",\n  \"expected_lock_version\": 25\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/reactivate", strings.NewReader("{\n  \"expected_credits\": 25,\n  \"expected_current_period_end\": \"2026-06-01T00:00:00Z\",\n  \"expected_lock_version\": 25\n}"))
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
req.Header.Set("Idempotency-Key", "reactivateAccountManagedSubscription-example-1")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
```

### Pause TokPortal Coverage

```http
POST /accounts/{id}/managed-subscription/cancel
```

Operation ID: `cancelAccountManagedSubscription`

Stops renewal without refunding payments. Account actions and analytics remain available through the paid/included period if no overdue Coverage is unpaid. Afterwards they are blocked until Coverage is restored; after 30 days of unavailability the API connection is removed. Resuming renewal during the current paid period is free. Otherwise reactivation costs exactly the unpaid 30-day periods shown by the Coverage status endpoint.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| Status | Description                                        |
| -----: | -------------------------------------------------- |
|  `200` | Renewal paused; paid-period access is preserved.   |
|  `401` | Missing, invalid, or revoked API key.              |
|  `404` | Account or coverage record not found.              |
|  `409` | Coverage cannot be cancelled in its current 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/cancel" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Idempotency-Key: cancelAccountManagedSubscription-example-1"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/cancel', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Idempotency-Key': 'cancelAccountManagedSubscription-example-1',
  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/cancel',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"], "Idempotency-Key": "cancelAccountManagedSubscription-example-1"}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/managed-subscription/cancel", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
req.Header.Set("Idempotency-Key", "cancelAccountManagedSubscription-example-1")
resp, err := http.DefaultClient.Do(req)
```

### List your delivered TikTok and Instagram accounts

```http
GET /accounts
```

Operation ID: `listAccounts`

Lists delivered saved accounts (real accounts that exist on the platform). Note the product has two ids: the account *listing* id carried by a bundle is stable across remakes, while the `saved_account_id` returned here is replaced every time an account is remade.

| Parameter  |    In | Required | Description                                                                                                                                                                   |
| ---------- | ----: | -------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `page`     | query |       No | Page number.                                                                                                                                                                  |
| `per_page` | query |       No | Items per page.                                                                                                                                                               |
| `platform` | query |       No | Filter by platform.                                                                                                                                                           |
| `country`  | query |       No | Filter by country code or country alias.                                                                                                                                      |
| `banned`   | query |       No | Filter by ban state. true returns only banned accounts (staff-validated or park-scan detected, matching the `banned` response field); false returns only non-banned accounts. |

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### List ban reports and appeals

```http
GET /account-bans
```

Operation ID: `listAccountBans`

Pollable list of validated ban reports for the caller's delivered accounts, covering the whole lifecycle: appeal\_pending (platform appeal filed, account unavailable but NOT yet banned), appeal\_accepted (account survived), appeal\_refused / no\_appeal\_banned (confirmed ban), and the staff commercial resolution (refund / remake / no\_remake with a machine-readable reason\_code such as tos\_ban). While a report is appeal\_pending, do NOT order a replacement account: a duplicate can trigger a ban-evasion strike on the platform, which is why TokPortal itself waits for the appeal. Key your reports on bundle\_id — account\_id can be null after a staff reset. A resolution of refund returns setup, warming and unused video slots, but not work already delivered and not the Coverage period, and the restored credits expire 60 days after restoration (see the credits.restored event in listWebhookEvents). This is the REST counterpart of the account.banned, account.ban\_appeal.submitted, account.ban\_appeal.resolved and account.ban\_resolution.decided webhook events. Only staff/CM-validated bans appear here — detections from TokPortal's internal health scan are never listed. Poll with the since parameter (updated\_at watermark) to pick up new reports and status changes.

| Parameter             |    In | Required | Description                                                                                                                 |
| --------------------- | ----: | -------: | --------------------------------------------------------------------------------------------------------------------------- |
| `page`                | query |       No | Page number.                                                                                                                |
| `per_page`            | query |       No | Items per page.                                                                                                             |
| `status`              | query |       No | Filter by appeal lifecycle status.                                                                                          |
| `resolution`          | query |       No | Filter by staff commercial resolution. 'pending' selects confirmed bans still awaiting the staff decision.                  |
| `account_id`          | query |       No | Filter by saved account ID.                                                                                                 |
| `since`               | query |       No | Only reports updated at or after this ISO 8601 timestamp. Use the highest updated\_at you have seen as a polling watermark. |
| `include_screenshots` | query |       No | When true, each report includes a signed 7-day URL of the ban-evidence screenshot when one exists.                          |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Paginated ban report 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/account-bans?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/account-bans?page=example&per_page=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/account-bans?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/account-bans?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`

Returns one delivered account: handle, platform, Coverage state, ban state and warming summary. A `saved_account_id` invalidated by a remake returns 404 — re-read the bundle to get the new one.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                                                                                                                                                                                                                                                                                                                                                                                                                              |
| -----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Delivered account details. For an eligible TikTok or Instagram account created before the global Coverage cutoff, managed\_subscription is null because the account is grandfathered. It can also be null for an ineligible platform or when no Coverage record exists. Otherwise, this stable technical TokPortal Coverage field contains the one per-saved-account status, period, unpaid periods, task access and reactivation price. |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Check the API connection of an account

```http
GET /accounts/{id}/connection
```

Operation ID: `getAccountConnection`

Returns the current API connection state, checked\_at, recovery action and separate feature eligibility. Ownership and current expected identity are checked. A fresh server check may be reused for up to 60 seconds and is invalidated by a newer disconnection event. An unavailable provider returns state checking, never a false disconnection. Messaging configuration eligible is not proof of an open reply window or successful send. This read does not create tasks or charge credits.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                                |
| -----: | ------------------------------------------ |
|  `200` | Connection state.                          |
|  `400` | Invalid account ID.                        |
|  `401` | Missing or invalid API key.                |
|  `404` | Account not found.                         |
|  `503` | Connection check unavailable. Retry later. |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/connection" \
  -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/connection', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/connection',
    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/connection", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read account messages or comments

```http
GET /accounts/{id}/inbox
```

Operation ID: `getAccountInbox`

Read-only inbox for one owned account with active API connection and eligible TokPortal Coverage. Posts/comments work for eligible Instagram and TikTok connections; conversations/messages require an eligible Instagram professional account (Creator or Business), granted messaging permissions and no current messaging restriction. First list posts or conversations, then pass the returned opaque reference for comments or messages. To read comment replies, keep the post reference and pass the comment's reference as reply\_reference. References and pagination cursors expire after one hour and are bound to the account, connection and resource. Refresh the parent list on inbox\_reference\_expired\_or\_invalid. Messages are newest first; reading never marks them as read. Comments may be cached upstream for 10 minutes. Replayed Instagram DM history is limited to the latest 500 conversations and 500 messages per conversation. This endpoint does not send, reply, moderate, create automations or charge credits. The separate /comments routes are paid human commenting tasks. Subscribe to inbox.comment.received and inbox.message.received for metadata notifications, then fetch this scoped endpoint. Public replies use POST /accounts/{id}/inbox/replies with reply\_action\_reference; this GET remains read-only. Instagram DM writes and private replies use the separate messages/private-replies endpoints; messaging metadata reports the current reply window.

| Parameter         |    In | Required | Description                                                                                                                                                                                          |
| ----------------- | ----: | -------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              |  path |      Yes | Saved account ID.                                                                                                                                                                                    |
| `kind`            | query |       No | Which inbox resource to read.                                                                                                                                                                        |
| `reference`       | query |       No | Opaque reference from a listed post or conversation. Required for comments and messages.                                                                                                             |
| `reply_reference` | query |       No | Opaque comment reference for reading its replies. Only for kind=comments, with the original post reference.                                                                                          |
| `cursor`          | query |       No | Pass pagination.next\_cursor verbatim with the same kind and references.                                                                                                                             |
| `limit`           | query |       No | Page size.                                                                                                                                                                                           |
| `min_comments`    | query |       No | For kind=posts: 1 hides posts with no comments, 0 includes them (default). Filtering is applied after pagination: keep following next\_cursor even on empty pages. Keep the same filter when paging. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Scoped inbox page.                                                                                       |
|  `400` | Invalid query or expired resource reference.                                                             |
|  `401` | Missing or invalid API key.                                                                              |
|  `404` | Account not found.                                                                                       |
|  `409` | Connection, messaging configuration, comments capability or Coverage is unavailable. Inspect error.code. |
|  `429` | Upstream rate limit. Retry later.                                                                        |
|  `503` | Inbox unavailable, including per-account upstream failures. An error is not an empty inbox.              |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox?kind=example&reference=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/inbox?kind=example&reference=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox?kind=example&reference=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/inbox?kind=example&reference=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read comment moderation rules and history

```http
GET /accounts/{id}/inbox/moderation
```

Operation ID: `getAccountCommentModeration`

Owner-scoped keyword hiding rules and the latest 20 processing results for Instagram or TikTok. Disabled by default. No private comment text is stored or returned. Use next\_cursor to read older history. post\_reference opens the owned comment thread using GET inbox kind=comments. Reading never hides or sends anything.

| Parameter |    In | Required | Description                                            |
| --------- | ----: | -------: | ------------------------------------------------------ |
| `id`      |  path |      Yes | Saved account ID.                                      |
| `cursor`  | query |       No | Opaque history next\_cursor; omit for the latest page. |

| Status | Description                         |
| -----: | ----------------------------------- |
|  `200` | Rules and history.                  |
|  `400` | Invalid cursor.                     |
|  `401` | Authentication required.            |
|  `404` | Account not found.                  |
|  `503` | Moderation temporarily 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation?cursor=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/inbox/moderation?cursor=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation?cursor=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/inbox/moderation?cursor=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Configure automatic keyword comment hiding

```http
PUT /accounts/{id}/inbox/moderation
```

Operation ID: `updateAccountCommentModeration`

Set keyword moderation for one owned Instagram or TikTok account. No DMs or credit charge. GET first, then pass settings.version for optimistic concurrency; a stale version returns moderation\_version\_conflict. Enabling requires a verified connection, comment access and usable Coverage. Disabling is always allowed for the owner. New signed comment events after saving are matched case-insensitively using Unicode NFKC; exceptions take priority, own comments are skipped. Whole-word mode treats letters, numbers and underscore as word characters. Hiding runs asynchronously after publication, normally immediately, with a five-minute recovery worker; comments can be visible before hiding. It cannot guarantee pre-publication filtering or delivery of every upstream webhook. Prior comments are not scanned. Rule changes cancel waiting older jobs. Provider quotas and shared per-account leases apply; Safe read verification can wait 330 seconds for newly received comments to become available. Processing is capped at three attempts; an external write is retried only after an explicit 429 refusal. Unknown writes are never resent automatically. Turning rules off does not restore hidden comments; TikTok unhide is currently unreliable. Dry-run validates without saving or hiding.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CommentModerationInput)

| Status | Description                                                            |
| -----: | ---------------------------------------------------------------------- |
|  `200` | Saved rules or dry-run.                                                |
|  `400` | Invalid settings.                                                      |
|  `401` | Authentication required.                                               |
|  `403` | Write permission required.                                             |
|  `404` | Account not found.                                                     |
|  `409` | Stale version, connection, Coverage or comment capability unavailable. |
|  `503` | Moderation temporarily 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X PUT "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false,
  "keywords": [
    "keywords"
  ],
  "exceptions": [
    "exceptions"
  ],
  "match_mode": "word",
  "version": 25
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "enabled": false,
  "keywords": [
    "keywords"
  ],
  "exceptions": [
    "exceptions"
  ],
  "match_mode": "word",
  "version": 25
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PUT',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"enabled\": false,\n  \"keywords\": [\n    \"keywords\"\n  ],\n  \"exceptions\": [\n    \"exceptions\"\n  ],\n  \"match_mode\": \"word\",\n  \"version\": 25\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/moderation", strings.NewReader("{\n  \"enabled\": false,\n  \"keywords\": [\n    \"keywords\"\n  ],\n  \"exceptions\": [\n    \"exceptions\"\n  ],\n  \"match_mode\": \"word\",\n  \"version\": 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)
```

### Moderate a received comment

```http
POST /accounts/{id}/inbox/comment-actions
```

Operation ID: `createAccountCommentAction`

Delete, hide, restore visibility, pin or unpin a comment on an owned post. Costs 0 credits. Only use actions returned on that comment by getAccountInbox, with their signed action.reference and the signed post reference. Capabilities are checked again before the write. Instagram supports delete/hide/unhide when allowed; TikTok supports delete/hide and top-level pin/unpin when allowed. TikTok deletion is generally limited to the connected account's own comments/replies. TikTok unhide and comment likes are not exposed in this release. Deletion is permanent and may also remove replies; ask the person using your integration for confirmation before deleting. Requires active Coverage, the current connection and write permission. Reuse the request\_key for the same intent, including transport retries. No bulk action or automatic retry of ambiguous results. Honor Retry-After and fetch operation status. Dry-run validates without modifying the platform. A provider acknowledgement is required for succeeded; an HTTP 200 alone is insufficient.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CommentActionInput)

| Status | Description                                                                       |
| -----: | --------------------------------------------------------------------------------- |
|  `200` | Durable result or validation-only result.                                         |
|  `400` | Invalid request or expired reference.                                             |
|  `401` | Authentication required.                                                          |
|  `403` | Write permission required.                                                        |
|  `404` | Account not found.                                                                |
|  `409` | Capability, Coverage or connection prevents this action, or idempotency conflict. |
|  `429` | Temporary quota or in-flight action. Honor Retry-After.                           |
|  `503` | Temporarily unavailable. Check operation state before retrying.                   |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "post_reference": "post_reference",
  "action_reference": "action_reference",
  "action": "delete",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "post_reference": "post_reference",
  "action_reference": "action_reference",
  "action": "delete",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"post_reference\": \"post_reference\",\n  \"action_reference\": \"action_reference\",\n  \"action\": \"delete\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions", strings.NewReader("{\n  \"post_reference\": \"post_reference\",\n  \"action_reference\": \"action_reference\",\n  \"action\": \"delete\",\n  \"request_key\": \"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)
```

### Read a comment moderation result

```http
GET /accounts/{id}/inbox/comment-actions/{actionId}
```

Operation ID: `getAccountCommentAction`

Read an owner-scoped durable action result. pending/unknown never authorizes an automatic resend. Deleted or absent comments alone do not prove this operation succeeded. No comment text is stored in the operation.

| Parameter  |   In | Required | Description                  |
| ---------- | ---: | -------: | ---------------------------- |
| `id`       | path |      Yes | Saved account ID.            |
| `actionId` | path |      Yes | Comment action operation ID. |

| Status | Description                     |
| -----: | ------------------------------- |
|  `200` | Current result.                 |
|  `400` | Invalid identifier.             |
|  `401` | Authentication required.        |
|  `404` | Account or operation not found. |
|  `503` | Status 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions/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/inbox/comment-actions/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/comment-actions/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/inbox/comment-actions/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read public comment reply limits

```http
GET /accounts/{id}/inbox/replies
```

Operation ID: `getAccountCommentReplyLimits`

Read the current account-wide public reply budget without sending or contacting the provider. Minimum 60 seconds between admitted attempts and maximum 50 per rolling hour across API keys, UI, MCP and reconnects. Deduplicated replays and locally blocked requests do not consume a new slot. Provider-refused or unconfirmed admitted attempts count conservatively. Honor retry\_after\_seconds/available\_at; paused means a confirmed platform spam restriction requires review before staff resume. This read does not lift a restriction or debit credits.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                |
| -----: | -------------------------- |
|  `200` | Account-wide reply budget. |
|  `401` | Authentication required.   |
|  `404` | Account not found.         |
|  `503` | Reply limits 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies" \
  -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/inbox/replies', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies',
    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/inbox/replies", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Reply to a received comment

```http
POST /accounts/{id}/inbox/replies
```

Operation ID: `createAccountCommentReply`

Reply publicly to one received comment on an owned Instagram or TikTok post. Costs 0 credits and never renews Coverage. Requires active Coverage, verified connection and can\_reply=true. Fetch posts, then comments; pass the post reference and the comment reply\_action\_reference. Reuse request\_key on an exact retry; an identical accepted/pending/unknown reply also replays across new keys. Minimum 60 seconds between admitted public reply attempts and maximum 50 per rolling hour per account, shared across UI/API/MCP and reconnects. Read GET on this path for current budget. Locally blocked requests and replays do not use a slot; admitted refused/unknown attempts count. HTTP 429 comment\_reply\_rate\_limited includes Retry-After; provider cooldowns can be longer. HTTP 409 comment\_replies\_paused follows an explicit platform spam restriction and requires review before staff resume. A succeeded operation has a reply\_id. pending/unknown means delivery is unconfirmed: fetch operation status and the comment thread, never blindly resend. Dry-run validates without sending. TikTok max 150 characters, Instagram max 2200. These guardrails do not guarantee immunity from platform restrictions.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (inline schema)

| Status | Description                                                     |
| -----: | --------------------------------------------------------------- |
|  `200` | Current result.                                                 |
|  `400` | Invalid request or expired inbox reference.                     |
|  `401` | Authentication required.                                        |
|  `402` | Insufficient credits for Business setup.                        |
|  `403` | Write permission required.                                      |
|  `404` | Account or operation not found.                                 |
|  `409` | Coverage, connection or eligibility prevents this action.       |
|  `429` | Per-account quota or in-flight action. Honor Retry-After.       |
|  `503` | Temporarily unavailable. Check operation state before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "post_reference": "post_reference",
  "comment_reference": "comment_reference",
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "post_reference": "post_reference",
  "comment_reference": "comment_reference",
  "text": "text",
  "request_key": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"post_reference\": \"post_reference\",\n  \"comment_reference\": \"comment_reference\",\n  \"text\": \"text\",\n  \"request_key\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies", strings.NewReader("{\n  \"post_reference\": \"post_reference\",\n  \"comment_reference\": \"comment_reference\",\n  \"text\": \"text\",\n  \"request_key\": \"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)
```

### Read a comment reply result

```http
GET /accounts/{id}/inbox/replies/{replyId}
```

Operation ID: `getAccountCommentReply`

Owner-scoped durable delivery state: pending, succeeded, failed, rate\_limited or unknown. No reply text or private credentials returned. Never automatically resend an unknown outcome.

| Parameter |   In | Required | Description         |
| --------- | ---: | -------: | ------------------- |
| `id`      | path |      Yes | Saved account ID.   |
| `replyId` | path |      Yes | Reply operation ID. |

| Status | Description                                                     |
| -----: | --------------------------------------------------------------- |
|  `200` | Current result.                                                 |
|  `400` | Invalid request or expired inbox reference.                     |
|  `401` | Authentication required.                                        |
|  `402` | Insufficient credits for Business setup.                        |
|  `403` | Write permission required.                                      |
|  `404` | Account or operation not found.                                 |
|  `409` | Coverage, connection or eligibility prevents this action.       |
|  `429` | Per-account quota or in-flight action. Honor Retry-After.       |
|  `503` | Temporarily unavailable. Check operation state before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies/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/inbox/replies/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/inbox/replies/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/inbox/replies/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read Instagram Business setup progress

```http
GET /accounts/{id}/business-setup
```

Operation ID: `getAccountBusinessSetup`

Client task status and 5-credit price. Manager compensation and private diagnostics are never returned. Completion verifies account identity, Business status, fresh authorization and messaging permissions; it does not prove that every recipient can receive a message.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                                                     |
| -----: | --------------------------------------------------------------- |
|  `200` | Current result.                                                 |
|  `400` | Invalid request or expired inbox reference.                     |
|  `401` | Authentication required.                                        |
|  `402` | Insufficient credits for Business setup.                        |
|  `403` | Write permission required.                                      |
|  `404` | Account or operation not found.                                 |
|  `409` | Coverage, connection or eligibility prevents this action.       |
|  `429` | Per-account quota or in-flight action. Honor Retry-After.       |
|  `503` | Temporarily unavailable. Check operation state before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup" \
  -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/business-setup', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup',
    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/business-setup", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Order Instagram Business setup

```http
POST /accounts/{id}/business-setup
```

Operation ID: `requestAccountBusinessSetup`

Requests a manager conversion and fresh connection for 5 credits, once per saved account. Requires write scope, active Coverage and current manager. Existing requests replay without a second debit; accounts already Business are not charged. Explicitly acknowledge restricted commercial music access. Manager payout is separate. Dry-run validates without charging or assigning a task.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (inline schema)

| Status | Description                                                     |
| -----: | --------------------------------------------------------------- |
|  `200` | Current result.                                                 |
|  `400` | Invalid request or expired inbox reference.                     |
|  `401` | Authentication required.                                        |
|  `402` | Insufficient credits for Business setup.                        |
|  `403` | Write permission required.                                      |
|  `404` | Account or operation not found.                                 |
|  `409` | Coverage, connection or eligibility prevents this action.       |
|  `429` | Per-account quota or in-flight action. Honor Retry-After.       |
|  `503` | Temporarily unavailable. Check operation state before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "music_limitations_acknowledged": true
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "music_limitations_acknowledged": true
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"music_limitations_acknowledged\": true\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/business-setup", strings.NewReader("{\n  \"music_limitations_acknowledged\": true\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)
```

### Update account commenting profile

```http
PATCH /accounts/{id}/commenting-profile
```

Operation ID: `updateAccountCommentingProfile`

Sets the internal commenting-autopilot metadata (niche, persona, goal, language, tracked handles) that TokPortal uses to draft comments for this account. It NEVER changes the account's public profile — use createAccountEditRequest to change a delivered account's username, visible name, biography, picture or link. The account must belong to the API key owner and must have active TokPortal Coverage, unless it is permanently grandfathered. Revealed/detached, banned, paused, lapsed and unrecoverable accounts are read-only.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (UpdateAccountCommentingProfileRequest)

| Status | Description                                                                                                          |
| -----: | -------------------------------------------------------------------------------------------------------------------- |
|  `200` | Updated account commenting profile.                                                                                  |
|  `400` | Invalid commenting profile body.                                                                                     |
|  `401` | Missing, invalid, or revoked API key.                                                                                |
|  `404` | Account not found.                                                                                                   |
|  `409` | Account mutation blocked. Inspect error.details.reason; reactivate TokPortal Coverage when the state is recoverable. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X PATCH "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/commenting-profile" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "niche": "niche",
  "persona": "persona",
  "goal": "promotion",
  "language": "language"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/commenting-profile', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "niche": "niche",
  "persona": "persona",
  "goal": "promotion",
  "language": "language"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/commenting-profile',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"niche\": \"niche\",\n  \"persona\": \"persona\",\n  \"goal\": \"promotion\",\n  \"language\": \"language\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/commenting-profile", strings.NewReader("{\n  \"niche\": \"niche\",\n  \"persona\": \"persona\",\n  \"goal\": \"promotion\",\n  \"language\": \"language\"\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 bundles for a delivered account

```http
GET /accounts/{id}/bundles
```

Operation ID: `listAccountBundles`

Lists every bundle attached to one delivered account, including cancelled ones. Use it to see an account's full order history before ordering more work on it.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Retrieving a verification code is the same irreversible first-access event as revealing credentials. Its policy is determined by this saved account's created\_at timestamp against the immutable managed\_pricing\_new\_customer\_cutover\_at value, never by the workspace action-pricing cohort or August 14 grace deadline. A saved account created before the cutoff permanently keeps the prior API contract: 0 credits, no new versioned acknowledgment body required, no TokPortal detachment, existing task access remains available, and support plus ban coverage end after access. A saved account created at or after the cutoff follows the managed policy: normally 150 credits, or the stored $10 legacy / $15 new per-30-day rate when Account Owning is already admin-approved. Managed access permanently detaches the account, makes it read-only, and ends TokPortal Coverage, task access, analytics updates, support, ban protection, replacement, refunds, compensation and credit restoration. For a new-policy account, missing explicit acceptance fails with 428 and returns the exact account-specific terms, price and policy version. A changed quote returns 409 before any debit or reveal. Reaching the Account Owning eligibility threshold only submits an admin review request; it never activates the agreement automatically. While approval is pending, reveal remains available for 150 credits on a post-cutoff account. Do not send Idempotency-Key: this response contains a verification secret and is never stored in the replay ledger. A request that includes the header is rejected before any ledger claim, reveal, debit, or inbox access with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400). After an uncertain transport result, fetch the safe account state before deciding whether to call this endpoint again without the header.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (AcknowledgeSupportForfeitRequest)

| Status | Description                                                                                                                                                                                                                                                                                             |
| -----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Verification code result.                                                                                                                                                                                                                                                                               |
|  `400` | Idempotency-Key is not supported because the response contains a verification secret. Remove the header and retry only after reconciling account state. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE.                                                                           |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                                   |
|  `402` | Insufficient credits for the applicable one-time reveal, or incomplete activation or billing for an admin-approved Account Owning Fee agreement.                                                                                                                                                        |
|  `404` | Account or verification code not found.                                                                                                                                                                                                                                                                 |
|  `409` | CREDENTIAL\_REVEAL\_QUOTE\_CHANGED: the submitted non-empty policy version or approved price snapshot is stale. Nothing was charged or revealed. Show the current disclosure and exact expected\_credit\_cost returned in error.details, obtain fresh consent, then retry with details.policy\_version. |
|  `428` | CREDENTIALS\_ACKNOWLEDGMENT\_REQUIRED: acceptance or its policy version is missing. Nothing was charged or revealed. Show error.details to the account owner, obtain consent, then retry with acknowledge\_support\_forfeit=true and details.policy\_version.                                           |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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" \
  -H "Content-Type: application/json" \
  -d '{
  "acknowledge_support_forfeit": true,
  "policy_version": "VERSION_RETURNED_BY_428"
}'
```

**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!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "acknowledge_support_forfeit": true,
  "policy_version": "VERSION_RETURNED_BY_428"
})
});
const data = await response.json();
```

**Python**

```python


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"]},
    json=json.loads("{\n  \"acknowledge_support_forfeit\": true,\n  \"policy_version\": \"VERSION_RETURNED_BY_428\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/verification-code", strings.NewReader("{\n  \"acknowledge_support_forfeit\": true,\n  \"policy_version\": \"VERSION_RETURNED_BY_428\"\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)
```

### Reveal delivered account credentials

```http
POST /accounts/{id}/reveal-credentials
```

Operation ID: `revealAccountCredentials`

The first credential or verification-code access is priced from this saved account's created\_at timestamp against the immutable managed\_pricing\_new\_customer\_cutover\_at value, never from the workspace action-pricing cohort or August 14 grace deadline. A saved account created before the cutoff permanently keeps the prior API contract: the reveal costs 0 credits, requires no new versioned acknowledgment body, and is irreversible; support and ban coverage end, but the account is not detached and existing TokPortal task access remains available. A saved account created at or after the cutoff follows the managed policy and requires the explicit versioned acknowledgment handshake: normally 150 credits, or the stored $10 legacy / $15 new per-30-day rate when Account Owning is already admin-approved. Crossing the Account Owning eligibility threshold only submits an admin review request and never activates it automatically. While approval is pending, reveal remains available for 150 credits on a post-cutoff account. Under the managed policy the charge is final and non-refundable, and reveal permanently detaches the account, makes it read-only, and ends TokPortal Coverage, all task access, analytics updates, support, ban protection, replacement, refunds, compensation and credit restoration. TokPortal is not responsible for later access, performance, reach, security, restrictions or bans. The debit or Account Owning activation, reveal marker and Coverage shutdown commit atomically. Previously revealed accounts are not charged a second time. Do not send Idempotency-Key: this response contains credentials and is never stored in the replay ledger. A request that includes the header is rejected before any ledger claim, reveal, debit, or secret access with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400). After an uncertain transport result, fetch the safe account state before deciding whether to call this endpoint again without the header.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (AcknowledgeSupportForfeitRequest)

| Status | Description                                                                                                                                                                                                                                                                                             |
| -----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Credentials reveal result.                                                                                                                                                                                                                                                                              |
|  `400` | Idempotency-Key is not supported because the response contains credentials. Remove the header and retry only after reconciling account state. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE.                                                                                     |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                                   |
|  `402` | Insufficient credits for the applicable one-time reveal, or incomplete activation or billing for an admin-approved Account Owning Fee agreement.                                                                                                                                                        |
|  `404` | Account not found.                                                                                                                                                                                                                                                                                      |
|  `409` | CREDENTIAL\_REVEAL\_QUOTE\_CHANGED: the submitted non-empty policy version or approved price snapshot is stale. Nothing was charged or revealed. Show the current disclosure and exact expected\_credit\_cost returned in error.details, obtain fresh consent, then retry with details.policy\_version. |
|  `428` | CREDENTIALS\_ACKNOWLEDGMENT\_REQUIRED: acceptance or its policy version is missing. Nothing was charged or revealed. Show error.details to the account owner, obtain consent, then retry with acknowledge\_support\_forfeit=true and details.policy\_version.                                           |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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" \
  -H "Content-Type: application/json" \
  -d '{
  "acknowledge_support_forfeit": true,
  "policy_version": "VERSION_RETURNED_BY_428"
}'
```

**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!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "acknowledge_support_forfeit": true,
  "policy_version": "VERSION_RETURNED_BY_428"
})
});
const data = await response.json();
```

**Python**

```python


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"]},
    json=json.loads("{\n  \"acknowledge_support_forfeit\": true,\n  \"policy_version\": \"VERSION_RETURNED_BY_428\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/reveal-credentials", strings.NewReader("{\n  \"acknowledge_support_forfeit\": true,\n  \"policy_version\": \"VERSION_RETURNED_BY_428\"\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 active account edit request

```http
GET /accounts/{id}/edit-request
```

Operation ID: `getAccountEditRequest`

Returns the account's profile edit requests, newest first, with their status and credit cost. Only one request can be open per account at a time, so check here before calling createAccountEditRequest.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Requests profile changes on an account that is already delivered and debits 8 credits. This is the ONLY way to change a delivered account's public username, visible name, biography, picture or link-in-bio: addEditSlots is unrelated (it buys video-editing slots) and configureBundleAccount only works before the account is delivered. Besides the username and visible name, supply at least one of requested\_biography, requested\_profile\_picture\_url or requested\_link\_in\_bio — a request that changes nothing else is rejected. Only one request can be open per account at a time (409 otherwise); check getAccountEditRequest first. Active TokPortal Coverage and a routable active account manager are required. TokPortal uses the account's current active manager, then its still-active manager relationship, then eligible non-cancelled order history. Completed bundles remain eligible without a delivery-age limit; cancelled orders are never restored. Assignment, debit, and task creation are atomic, so an unavailable manager never costs credits. The task appears directly in that manager's calendar.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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` | Coverage is inactive, no active account manager or non-cancelled support order is available, or an active edit request already exists. No credits are charged on these failures. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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",
  "requested_biography": "requested_biography"
}'
```

**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",
  "requested_biography": "requested_biography"
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"requested_username\": \"requested_username\",\n  \"requested_visible_name\": \"requested_visible_name\",\n  \"requested_biography\": \"requested_biography\"\n}")
)
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  \"requested_biography\": \"requested_biography\"\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)
```

## Profile

### Start here: your TokPortal workspace, credit balance and access

```http
GET /me
```

Operation ID: `getCurrentUser`

Returns the authenticated workspace: profile, role, analytics access tier and live credit balance. Call once at the start of a session to learn what this key can do.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Update safe workspace settings

```http
PATCH /me
```

Operation ID: `updateCurrentUserSettings`

Updates client-owned workspace profile fields used by Operator context. Does not expose auth, role, credit, staff, or manager settings.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (UpdateCurrentUserSettingsRequest)

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Updated safe workspace settings.      |
|  `400` | Invalid workspace setting.            |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X PATCH "https://app.tokportal.com/api/ext/me" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "company_name": "company_name",
  "website": "website",
  "company_niche": "company_niche",
  "organic_strategy": "organic_strategy"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/me', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "company_name": "company_name",
  "website": "website",
  "company_niche": "company_niche",
  "organic_strategy": "organic_strategy"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/me',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"company_name\": \"company_name\",\n  \"website\": \"website\",\n  \"company_niche\": \"company_niche\",\n  \"organic_strategy\": \"organic_strategy\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/me", strings.NewReader("{\n  \"company_name\": \"company_name\",\n  \"website\": \"website\",\n  \"company_niche\": \"company_niche\",\n  \"organic_strategy\": \"organic_strategy\"\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)
```

## Reference

### List the countries accounts can be created in

```http
GET /countries
```

Operation ID: `listCountries`

Lists the valid `country` values for bundle creation. Most are ISO-3166-1 alpha-2 (FR, DE, BR), but TokPortal's own codes are the authority and two of them are not alpha-2: the United States is `USA` and the United Kingdom is `UK`. Their ISO aliases `US` and `GB` are accepted and resolved to those two, so send a code from this list verbatim and never assume alpha-2. Call before createBundle or createBundlesBulk: `country` is validated against this list, and a full country name such as "United States" is rejected. `videos_only_countries` applies only to video orders on an existing account and is never valid for /bundles/bulk.

| Status | Description                                                                                                                                                                                                                                          |
| -----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Country availability by order type. data contains countries available for new account creation; videos\_only\_countries contains countries available for video orders on an existing account. Fully disabled countries are excluded from both lists. |

#### 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


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`

Lists the orderable platforms and, per video type, the exact required and optional fields. Call before configuring any slot: TikTok carousels require `tiktok_sound_url`, Instagram posts and reels require `instagram_content_type`, and a story takes exactly one of `video_url` or `story_image_url` and no description. Only TikTok and Instagram can be ordered; YouTube fields exist on the video schema but YouTube bundles are rejected with YOUTUBE\_DELAYED.

| 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


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

### See current prices (in credits)

```http
GET /credit-costs
```

Operation ID: `getCreditCosts`

Authoritative live credit prices and workspace cohort. Call it immediately before any purchase and never hard-code or recall a price. Not every key is orderable: `youtube_account_creation` is listed for reference only (YouTube bundles are rejected with YOUTUBE\_DELAYED), and `niche_warming` and `deep_warming` are legacy prices published for backward compatibility only — they are not products a caller may choose, and `advanced_warming` (Advanced Niche Warming, priced per niche target) is the only warming TokPortal sells. `managed_account_subscription` (TokPortal Coverage) recurs every 30 days per account after the included first period, so a multi-account quote must include it as a monthly cost. There is no dry-run endpoint: assemble the total from this table (account setup + video slots + Advanced Niche Warming targets + edit slots + paid video options), apply the returned cohort and contract\_bundle\_allowance, and confirm it with the user before creating anything.

| Status | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | The authenticated workspace's effective action-price table, its cohort and transition state, both cohort dates, TokPortal Coverage price and account eligibility, the saved-account credential-reveal cutoff, Account Owning Fee approval threshold and legacy/new agreement rates, and the 1-credit Comments API task. credential\_reveal is an informational fallback only: the exact first-access quote is account-specific and is returned by HTTP 428 or 409 from the credential or verification-code endpoint. The former per-bundle moderation add-on is unavailable. managed\_account\_subscription remains the stable technical cost key. |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/credit-costs" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/credit-costs', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/credit-costs',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/credit-costs", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### See the credit balance

```http
GET /credits/balance
```

Operation ID: `getCreditBalance`

Returns the currently spendable credit balance and upcoming expirations. Check it before any purchase: a 402 on a mutation means this was short.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Paginated ledger of every credit debit and refund, newest first. Use it to reconcile what a bundle or batch actually cost, since server-side pricing (workspace cohort, contract allowance) can differ from the list price.

| Parameter   |    In | Required | Description                                                 |
| ----------- | ----: | -------: | ----------------------------------------------------------- |
| `page`      | query |       No | Page number.                                                |
| `per_page`  | query |       No | Items per page.                                             |
| `date_from` | query |       No | Filter transactions created on or after this date.          |
| `date_to`   | query |       No | Filter transactions created before the day after this date. |

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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 your account orders (bundles) and their next step

```http
GET /bundles
```

Operation ID: `listBundles`

Lists your bundles with their lifecycle status and the computed `next_action` (`configure_account`, `configure_videos`, `publish_bundle`, or null). `next_action` is the recommended way to find what a bundle still needs. Video counters cover all slots and are independent of page/per\_page: `videos_quantity` is the recorded slot quantity, `videos_total` counts existing slots, and `videos_configured_count` includes configured, published, accepted, in\_review, pending\_corrections and finalized slots. Unused slots have status `pending`; with the current status set their count is `videos_total - videos_configured_count`. Configured or finalized slots are not unused capacity. A failed video read returns an error, never zero counters.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Get a new TikTok or Instagram account (created, warmed and posted on by TokPortal's team)

```http
POST /bundles
```

Operation ID: `createBundle`

Use this when the user wants a new TikTok or Instagram account, or more posts on an account TokPortal already runs (videos\_only). TokPortal's human account managers create the account on their own devices, warm it up and post the scheduled videos, so you never sign up for or log into anything yourself: this call is the paid order. Typical first order: account\_and\_videos with Advanced Niche Warming at 3 targets and about 25 videos; every quantity can be adjusted. Creates and pays an account-only, account-and-videos, or videos-only bundle in one atomic creation checkout. Credits are debited immediately on a successful POST, not when the bundle is published, and THERE IS NO CANCELLATION OR REFUND PATH — read getCreditCosts and getCreditBalance and confirm the total with the user before calling. One bundle produces exactly one account; order several accounts with createBundlesBulk. TikTok and Instagram only: a YouTube bundle is rejected with YOUTUBE\_DELAYED even though getCreditCosts lists a YouTube price. Advanced Niche Warming requires wants\_advanced\_warming: true ALONGSIDE advanced\_warming\_terms or advanced\_warming\_terms\_count; targets sent without the flag are refused with ADVANCED\_WARMING\_FLAG\_REQUIRED, never charged and dropped. Send the term list or the count, not two quantities that disagree (ADVANCED\_WARMING\_COUNT\_MISMATCH), and send distinct terms — anything removed by cleanup fails the call with ADVANCED\_WARMING\_TERMS\_REJECTED rather than silently reducing what you receive and pay for. Credit cost is calculated server-side from the workspace cohort and any contract\_bundle\_allowance returned by getCreditCosts. A qualifying allowance slot is consumed in the same transaction as the credit debit; failed requests consume nothing. The exact new-customer and saved-account cutoff is the immutable production PRE-migration instant returned by getCreditCosts. A real TikTok or Instagram saved account created on or after that cutoff receives its first 30 days of TokPortal Coverage included unless the creation response marks it contractually exempt, and Coverage then recurs every 30 days per account at the managed\_account\_subscription price. For Advanced Niche Warming, pass advanced\_warming\_terms now, or buy a target count with advanced\_warming\_terms\_count at the effective workspace rate and configure the targets later via PUT /bundles/{id}/warming-terms — one or the other, always with wants\_advanced\_warming: true. Set the targets BEFORE publishing: publishBundle refuses a bundle whose warming has no targets (ADVANCED\_WARMING\_NOT\_CONFIGURED), and a purchase left unconfigured is not refunded. The bundle is created in pending\_setup and still needs configureBundleAccount and publishBundle. Delivered videos are approved automatically unless you send auto\_finalize\_videos: false — the response echoes the settlement mode as review\_mode ('auto' or 'manual'), plus a one-off review\_mode\_notice when you left the choice to the default. external\_ref is a client correlation and duplicate-detection aid, not a retry mechanism; only Idempotency-Key provides exact request replay.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CreateBundleRequest)

| Status | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| -----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Bundle created.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
|  `400` | Invalid request body or business rule violation. Warming-specific codes: ADVANCED\_WARMING\_FLAG\_REQUIRED (advanced\_warming\_terms or advanced\_warming\_terms\_count sent without wants\_advanced\_warming: true), ADVANCED\_WARMING\_TERMS\_REJECTED (a term was removed as a duplicate or for its length, so fewer targets would have been delivered and billed than requested — details.dropped\_terms lists them), ADVANCED\_WARMING\_COUNT\_MISMATCH (advanced\_warming\_terms and advanced\_warming\_terms\_count state different quantities), ADVANCED\_WARMING\_TERMS (count outside 3-30 or not a multiple of 3), ADVANCED\_WARMING\_PLATFORM, WARMING\_CONFLICT. UNKNOWN\_FIELD when the body carries a field TokPortal does not recognise: this write is a full replace, so an unrecognised field is refused rather than dropped. |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|  `402` | Insufficient credits.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|  `409` | Duplicate account bundle, invalid state, or BUNDLE\_PRICING\_CHANGED. A pricing change charges nothing and creates nothing; fetch getCreditCosts again and retry with a new Idempotency-Key.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
|  `429` | Workspace bundle capacity is on cooldown. Retry later.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/bundles',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"bundle_type\": \"account_only\"\n}")
)
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)
```

### Get several new TikTok or Instagram accounts at once (created by TokPortal's team)

```http
POST /bundles/bulk
```

Operation ID: `createBundlesBulk`

Use this when the user wants several new TikTok and/or Instagram accounts. TokPortal's human account managers create and run them, so you never sign up for anything yourself: this call is the paid order. Creates and pays several bundles in one atomic creation checkout. Credits are debited immediately on a successful POST, not at publication, and THERE IS NO CANCELLATION OR REFUND PATH — read getCreditCosts and getCreditBalance and confirm the total with the user before calling, because the loss is multiplied by accounts\_count and platforms. Each bundle produces exactly one account. Warming targets and credits are charged per account, not per batch. Advanced Niche Warming requires wants\_advanced\_warming: true ALONGSIDE advanced\_warming\_terms or advanced\_warming\_terms\_count; targets sent without the flag are refused with ADVANCED\_WARMING\_FLAG\_REQUIRED for the whole batch, never charged and dropped. Send the term list or the count, not two quantities that disagree (ADVANCED\_WARMING\_COUNT\_MISMATCH), and send distinct terms — anything removed by cleanup fails the call with ADVANCED\_WARMING\_TERMS\_REJECTED. The complete batch is checked against rolling workspace capacity and any contract\_bundle\_allowance returned by getCreditCosts. Qualifying allowance slots are consumed in deterministic bundle-ID order in the same transaction as all debits; if any bundle fails, the entire batch rolls back and consumes nothing, so never split a 429 capacity-cooldown batch into parallel calls. A batch crossing the final allowance slot receives the contract price only for the remaining qualifying bundles. For Advanced Niche Warming, pass advanced\_warming\_terms, using the same terms for every account, or buy a per-account target count with advanced\_warming\_terms\_count and configure each bundle's targets later via PUT /bundles/{id}/warming-terms — one or the other, always with wants\_advanced\_warming: true. Every bundle is returned in pending\_setup with no username: each one still needs configureBundleAccount and publishBundle before any work starts. Delivered videos are approved automatically for every bundle in the batch unless you send auto\_finalize\_videos: false — the response echoes review\_mode ('auto' or 'manual'), plus a one-off review\_mode\_notice when you left the choice to the default. external\_ref is one optional batch-level reference copied to the resulting bundles, not a unique per-bundle key or a retry mechanism; use returned bundle IDs for identity and Idempotency-Key for exact replay.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CreateBulkBundlesRequest)

| Status | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| -----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Bulk bundle creation result.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
|  `400` | Invalid request body or business rule violation. Warming-specific codes: ADVANCED\_WARMING\_FLAG\_REQUIRED (advanced\_warming\_terms or advanced\_warming\_terms\_count sent without wants\_advanced\_warming: true), ADVANCED\_WARMING\_TERMS\_REJECTED (a term was removed as a duplicate or for its length, so fewer targets would have been delivered and billed than requested — details.dropped\_terms lists them), ADVANCED\_WARMING\_COUNT\_MISMATCH (advanced\_warming\_terms and advanced\_warming\_terms\_count state different quantities), ADVANCED\_WARMING\_TERMS (count outside 3-30 or not a multiple of 3), ADVANCED\_WARMING\_PLATFORM, WARMING\_CONFLICT. UNKNOWN\_FIELD when the body carries a field TokPortal does not recognise: this write is a full replace, so an unrecognised field is refused rather than dropped. |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|  `402` | Insufficient credits.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|  `409` | BUNDLE\_PRICING\_CHANGED means the full batch was rolled back with no charge and no allowance consumption. In details, `required` is the recomputed price of ONE bundle (the first that disagreed) while `quoted` is the total for the whole batch — `required_scope` and `quoted_scope` label which is which, and `batch_bundles` carries the count. Fetch getCreditCosts again and retry with a new Idempotency-Key.                                                                                                                                                                                                                                                                                                                                                                                                                          |
|  `429` | The full batch would exceed rolling workspace capacity. Retry later.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/bundles/bulk',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"platforms\": [\n    \"tiktok\"\n  ],\n  \"country\": \"USA\",\n  \"accounts_count\": 25\n}")
)
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 one account order (bundle) in detail

```http
GET /bundles/{id}
```

Operation ID: `getBundle`

Full state of one bundle: status, account configuration, video slot counts, warming purchase, cancellation and remake history, and `next_action`. A remade bundle keeps the same id while its `saved_account_id` changes. Includes all video slots, including bundles with more than 1,000 slots. For unused capacity, count `videos` with status `pending`; `videos_configured_count` includes slots that have since been published or finalized. Counters have the same meaning as listBundles.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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: title, external\_ref, and auto\_finalize\_videos. auto\_finalize\_videos true (the default) approves every delivered video automatically — it counts as final on arrival; set it to false to hold each delivered video for your review, approving with finalizeBundleVideo or pushing back with requestBundleVideoCorrections yourself. Note that anything left in review is auto-finalized about 72 hours after entering it either way.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"auto_finalize_videos\": false,\n  \"external_ref\": \"external_ref\",\n  \"title\": \"Launch campaign\"\n}")
)
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)
```

### Start the work: hand the account and its posts to TokPortal's team

```http
POST /bundles/{id}/publish
```

Operation ID: `publishBundle`

Submits a fully configured bundle to the account-manager marketplace, which is what actually starts the work. Requires status `pending_setup`, a complete account profile, and at least one configured video on an `account_and_videos` bundle — call getBundlePublishReadiness first, it returns every blocker without debiting. A due Coverage period renews here, so this call can debit credits even though the bundle's own cost was charged at creation. Irreversible once a manager accepts: unpublishBundle only works before that, and never refunds. Publish-time date guard: any slot whose target date has already reached today or the past is moved forward to the earliest allowed day (today+1 with a delivered or existing account, today+3 while the account is still being created, in UTC), respecting the 3-videos-per-day-per-bundle cap — an overflowing day spills to the next one. Slots still in the future are NOT moved. This never fails the publish. When something moved, the response carries `adjusted_videos` — one entry per moved slot, `{video_id, position, previous_date, new_date}` — and `adjusted_videos_note`. Read them back instead of assuming the schedule you sent survived. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |
|  `429` | Workspace publication capacity is on cooldown. Retry later.                                                                                                                                                                                                                           |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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 what is still missing before starting the work

```http
GET /bundles/{id}/publish-readiness
```

Operation ID: `getBundlePublishReadiness`

Returns every current publish blocker without mutating the bundle, settling Coverage, or debiting credits. MANAGED\_ACCOUNT\_TASK\_BLOCKED includes details.account\_id and details.reason. Fetch GET /accounts/{account\_id}/managed-subscription for the authoritative state and exact reactivation quote. A due included or active period can temporarily appear blocked until renewal is settled by cron or an approved mutation.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Returns a published bundle to `pending_setup` so its account configuration can be edited again. Only works while the bundle is published and no account manager has accepted it (409 otherwise). Credits are NOT refunded and this is not a cancellation: there is no API path to cancel a bundle or recover its credits.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Buys additional video slots on an existing bundle and debits credits immediately (see `video_upload` in getCreditCosts). The slots are non-refundable and there is no cancellation path for this purchase; confirm the exact cost with the user before calling. Allowed bundle statuses: `pending_setup`, `published`, `published_priority`, `accepted`. An `account_only` or `completed` bundle cannot receive more slots; create a `videos_only` bundle for the saved account instead, subject to account eligibility. Configure the new positions with configureBundleVideo, then publish them.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"quantity\": 1\n}")
)
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 VIDEO-editing slots to a bundle

```http
POST /bundles/{id}/add-edit-slots
```

Operation ID: `addEditSlots`

Buys video-editing slots — professional editing of a video by the account manager — and debits credits immediately (see `video_edit` in getCreditCosts). Non-refundable, with no cancellation path; confirm the exact cost with the user before calling. `edits_quantity` cannot exceed the bundle's `videos_quantity`, one video consumes at most one slot, and a slot is only consumed when that video carries `editing_instructions`. This has nothing to do with editing an account profile — use createAccountEditRequest for that.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"quantity\": 1\n}")
)
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`

Returns the current account-profile configuration of a bundle (username, visible name, biography, picture) and its listing status. Read it before configureBundleAccount to see what is already set and whether the profile is still editable.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Set up a new account's profile (username, display name, bio, photo)

```http
PUT /bundles/{id}/account
```

Operation ID: `configureBundleAccount`

Sets the profile of an account that has NOT been delivered yet: username, visible name, biography, profile picture, and link-in-bio on Instagram bundles. Only valid while the bundle is `pending_setup` or `configured` — unpublish first to edit after publishing, and use createAccountEditRequest for an account that is already delivered. This is a full replacement: any field you omit is written back as null, so re-send the whole profile when you change one part of it. `link_in_bio` is stored on Instagram bundles only. On any other platform it is accepted but not stored, and the response says so explicitly through `_warnings` while echoing `link_in_bio: null` — read both back. For backward compatibility this endpoint also accepts the exact purchased advanced\_warming\_terms list, but PUT /bundles/{id}/warming-terms is the recommended one-shot deferred-target endpoint because it remains available at any account status. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription. A new account that has not produced a saved account yet remains configurable before delivery.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (ConfigureAccountRequest)

| Status | Description                                                                                                                                                                                                                                                                                                                                                |
| -----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Configured account.                                                                                                                                                                                                                                                                                                                                        |
|  `400` | Invalid account profile, or advanced\_warming\_terms does not match the purchased target count (ADVANCED\_WARMING\_TERMS). UNKNOWN\_FIELD when the body carries an unrecognised field: this is a full replace, so a misspelled field would otherwise erase the value it was meant to set.                                                                  |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                                                                                      |
|  `404` | Bundle or account not found, or no warming session to configure (WARMING\_SESSION\_NOT\_FOUND).                                                                                                                                                                                                                                                            |
|  `409` | Account status does not allow configuration, or the warming targets are already set (WARMING\_TERMS\_ALREADY\_SET). If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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": "USA",
  "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": "USA",
  "visible_name": "visible_name"
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"username\": \"USA\",\n  \"visible_name\": \"visible_name\"\n}")
)
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\": \"USA\",\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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"comment\": \"Please review this item.\"\n}")
)
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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Lists every video slot of a bundle with its position, configuration, schedule and review status, including bundles with more than 1,000 slots. Unused slots have status `pending`; configured, scheduled and finalized slots are already allocated. Positions are 1-based and run up to the bundle's `videos_quantity`. Every video carries `platform_signals`, what the platform did with the live post once it exists: `not_recommended` (TikTok Not For Feed / Not Recommended — online but no distribution), `ai_label` (`creator` | `platform`), `sound` (`ok` | `non_commercial` | `removed` | `muted`) with `sound_reason`, and `checked_at`. All fields are null until the post has been checked (\~48 h after posting, again at 7 days); `null` means unknown, never 'no'. The same events arrive as `video.not_recommended`, `video.ai_labeled` and `video.sound_restricted` webhooks.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Returns one video slot's configuration, schedule, review status and post link, plus `platform_signals` (TikTok not-recommended flag, AI label, sound status — null until the live post has been checked). Use it to check whether a slot is still inside its review window before finalizing it, or whether the platform is distributing the post at all.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Schedule one post (video or carousel): media, caption, date

```http
PUT /bundles/{id}/videos/{position}
```

Operation ID: `configureBundleVideo`

Configures one slot as a video, carousel, or story, and schedules it. A story (TikTok/Instagram only) is a single video OR a single image (provide exactly one of video\_url or story\_image\_url), has no description, and is verified by an account-manager screenshot. Platform-specific fields are only stored on their own platform: `tiktok_sound_url` on TikTok, every `instagram_*` field on Instagram, every `youtube_*` field on YouTube. A field belonging to another platform is accepted and not stored, but it is no longer discarded in silence: the response lists those names in `ignored_fields`, next to `platform`, with an `ignored_fields_note` explaining why. Check `ignored_fields` on every 200 — an empty or absent list is the only proof that everything you sent landed. Some options are paid and charged the first time they are set, never refunded: story\_repost\_url, instant\_repost\_as\_story, and setting either volume field. Scheduling: `target_publish_date` is the FIRST day of a 2-day publishing window, never a fixed date — the end day is derived as that day + 1 and cannot be chosen, so `target_publish_end_date` is refused here with a hint pointing at PATCH. The window that was actually stored comes back as `target_publish_start_date` / `target_publish_end_date`. `auto_publish` is per-video HERE and top-level-only on the batch endpoint. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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",
  "target_publish_date": "2026-06-01"
}'
```

**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",
  "target_publish_date": "2026-06-01"
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"video_type\": \"video\",\n  \"target_publish_date\": \"2026-06-01\"\n}")
)
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  \"target_publish_date\": \"2026-06-01\"\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 metadata or schedule

```http
PATCH /bundles/{id}/videos/{position}
```

Operation ID: `patchBundleVideo`

Changes a configured slot's name, description, external reference or schedule without re-sending its media. Rescheduling obeys the maximum of 3 videos per day per bundle AND — enforced since 2026-08-26, where before only the per-day cap ran — the same minimum publish lead time as configuration: today+1 with a delivered or existing account, today+3 while the account is still being created, in UTC. Today and any past date are rejected with `INVALID_DATE` carrying `min_days_ahead` and `earliest_allowed` (YYYY-MM-DD), the same shape the configure PUT returns. Send either target\_publish\_date (a 2-day window whose end is derived as that day + 1) or the target\_publish\_start\_date/target\_publish\_end\_date pair, never both — this is the ONLY endpoint where the window's end can be chosen, and it must fall strictly after the start. Cannot be used once the video is finalized. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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.                                                                                                                                                                                                                                                                 |
|  `409` | Video status or account state does not allow this patch. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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",
  "description": "Campaign content description.",
  "target_publish_date": "2026-06-01"
}'
```

**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",
  "description": "Campaign content description.",
  "target_publish_date": "2026-06-01"
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"external_ref\": \"external_ref\",\n  \"name\": \"name\",\n  \"description\": \"Campaign content description.\",\n  \"target_publish_date\": \"2026-06-01\"\n}")
)
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  \"description\": \"Campaign content description.\",\n  \"target_publish_date\": \"2026-06-01\"\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)
```

### Schedule several posts at once

```http
PUT /bundles/{id}/videos/batch
```

Operation ID: `batchConfigureBundleVideos`

Configures many video slots in one call after a single account-level Coverage preflight. Partial success: this call returns HTTP 200 even when some or all rows are rejected. Read `configured`, `results` and `errors[]` and never infer success from the status code — rows breaking the 3-videos-per-day cap or the minimum lead time fail individually while the rest succeed, and the paid options they carry are only charged for the rows that succeeded. An `auto_publish` failure never fails the request; inspect `auto_publish.blockers`. TOP-LEVEL ONLY on this endpoint: one publish attempt per call, applying to every video in it. auto\_publish inside videos\[] is rejected with a hint telling you to move it out — it is per-video only on the single-slot PUT /bundles/{id}/videos/{position}. Each row's `target_publish_date` is the first day of a 2-day window whose end is derived (that day + 1); `target_publish_end_date` is refused on this endpoint. Publish-time date guard: any slot whose target date has already reached today or the past is moved forward to the earliest allowed day (today+1 with a delivered or existing account, today+3 while the account is still being created, in UTC), respecting the 3-videos-per-day-per-bundle cap — an overflowing day spills to the next one. Slots still in the future are NOT moved. This never fails the publish. When something moved, the response carries `adjusted_videos` — one entry per moved slot, `{video_id, position, previous_date, new_date}` — and `adjusted_videos_note`. Read them back instead of assuming the schedule you sent survived. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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.                                                                                                                                                                                                                                                                                                 |
|  `409` | One or more video slots or the account state does not allow configuration. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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",
      "target_publish_date": "2026-06-01",
      "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",
      "target_publish_date": "2026-06-01",
      "position": 1
    }
  ]
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"videos\": [\n    {\n      \"video_type\": \"video\",\n      \"target_publish_date\": \"2026-06-01\",\n      \"position\": 1\n    }\n  ]\n}")
)
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      \"target_publish_date\": \"2026-06-01\",\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`

Hands every configured, unpublished slot on an active bundle to the account manager for posting. Publishing is irreversible once a manager has posted; call unscheduleBundleVideo beforehand to hold a slot back. Slots whose target date has already reached today or the past are moved forward to the earliest allowed day, respecting the 3-videos-per-day-per-bundle cap (an overflowing day spills to the next); slots still in the future are left alone, and the publish never fails because of it. When something moved, the response carries `dates_adjusted` (how many), `adjusted_videos` (one `{video_id, position, previous_date, new_date}` per moved slot) and `hint`. There is no `earliest_allowed_date` field on this response — it was documented but never returned, and `adjusted_videos` replaces it with the per-slot dates. Read them back instead of assuming the original schedule survived. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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 the referenced media, and configures the available video slots. Partial success: this call returns HTTP 200 even when some or all rows are rejected. Read `imported`, `results` and `errors[]` and never infer success from the status code — rows breaking the 3-videos-per-day cap or the minimum lead time fail individually while the rest succeed. An `auto_publish` failure never fails the request; inspect `auto_publish.blockers`, and `auto_publish.adjusted_videos` when a stale date was moved forward at publish time. The date column is `target_publish_start_date` (note the name: the JSON endpoints call the same value `target_publish_date`) and it is the FIRST day of a 2-day window — the end is derived as that day + 1, so a `target_publish_end_date` column is refused with CSV\_PARSE\_ERROR rather than dropped. `auto_publish` is a form field on the multipart body and covers the whole import. Coverage is checked before TokPortal fetches or stores any referenced media. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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.                                                                                                                                                                                                                                                                             |
|  `409` | The account or bundle state does not allow CSV import. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Hands one configured slot to the account manager for posting on its target date. Irreversible once the manager has posted. If that target date has already reached today or the past it is moved forward to the earliest allowed day rather than published as instantly-due work, and the response then carries `date_adjusted: true`, `original_date`, `new_date` (both YYYY-MM-DD) and `hint`. Those four fields were documented long before they were real: until 2026-08-26 the RPC never returned them, and the slot published on its stale date. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Clears a slot's configuration so it can be configured again. Only works from `pending` or `configured`: to clear a published or accepted slot call unscheduleBundleVideo first, then reset. The slot's credits are not refunded and any paid option already charged on it stays charged. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Pulls a published slot back out of the account-manager queue and returns it to `configured`, so it can be rescheduled or reset. Does not refund the slot. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Approves a delivered video and releases the account manager's payout — the review window cannot be reopened afterwards. Only valid while the slot is `in_review`. Any slot left in review is auto-finalized about 72 hours after entering it, so act inside that window. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Sends a delivered video back to the account manager with written feedback instead of approving it. Only valid while the slot is `in_review`, i.e. inside the \~72-hour window before it auto-finalizes. The response does not echo `fields`, so keep your own record of what you flagged. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"comment\": \"Please review this item.\"\n}")
)
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. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `position`            |   path |      Yes | 1-based video slot position.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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. If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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 '{
  "video_url": "https://example.com/resource"
}'
```

**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({
  "video_url": "https://example.com/resource"
})
});
const data = await response.json();
```

**Python**

```python


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=json.loads("{\n  \"video_url\": \"https://example.com/resource\"\n}")
)
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("{\n  \"video_url\": \"https://example.com/resource\"\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 an ad code (TikTok Spark Code / Instagram Partner Code) for a finalized video

```http
POST /videos/{id}/ad-code-request
```

Operation ID: `createVideoAdCodeRequest`

Requests an ad code (TikTok Spark Code or Instagram Partner Code) for one finalized video and debits 7 credits, which are not refundable. The platform is resolved from the video itself and cannot be chosen. The video must be finalized on TikTok or Instagram with a live post link. Active TokPortal Coverage and a routable active account manager are required. TokPortal uses the account's current active manager, then its still-active manager relationship, then eligible non-cancelled order history. Completed bundles remain eligible without a delivery-age limit; cancelled orders are never restored. Assignment, debit, and task creation are atomic. Only one open request per video is allowed, so a request made on the wrong video cannot be replaced until it closes. Poll the GET endpoint to retrieve the code once the manager submits it.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Video ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (AdCodeRequest)

| Status | Description                                                                                                                                                                                           |
| -----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Ad code request created.                                                                                                                                                                              |
|  `400` | Invalid request or platform not available.                                                                                                                                                            |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                 |
|  `402` | Insufficient credits.                                                                                                                                                                                 |
|  `404` | Video not found.                                                                                                                                                                                      |
|  `409` | Coverage is inactive, no active account manager or non-cancelled support order is available, the video is not finalized, or an open request already exists. No credits are charged on these failures. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "note"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "note": "note"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"note\": \"note\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request", strings.NewReader("{\n  \"note\": \"note\"\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 / poll the ad code for a video

```http
GET /videos/{id}/ad-code-request
```

Operation ID: `getVideoAdCodeRequest`

Returns the latest ad code request for the video. The `code` field is null until the manager submits it (status `in_review`) or it is delivered (status `finalized`).

| Parameter |   In | Required | Description |
| --------- | ---: | -------: | ----------- |
| `id`      | path |      Yes | Video ID.   |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Latest ad code request (or null).     |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request" \
  -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/ad-code-request', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-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/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/ad-code-request", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Archive a finalized video (hide it from the public profile)

```http
POST /videos/{id}/archive-request
```

Operation ID: `createVideoArchiveRequest`

Asks the account manager to hide one finalized video from the public profile: set to "Only you" on TikTok, archived on Instagram. The video is not deleted. Debits 1 credit. The video must be finalized on TikTok or Instagram with a post link. Active TokPortal Coverage and an active account manager are required, with the same rules as account edit and ad code requests. Coverage validation, manager assignment, the debit and task creation are atomic: a failed prerequisite never charges. Only one open request per video is allowed and a video is archived at most once. After the manager confirms, TokPortal checks that the post is no longer public before marking it archived. Poll the GET endpoint until `archived` is true. No body is required.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Video ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| Status | Description                                                                                                                                                                                                                   |
| -----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Archive request created.                                                                                                                                                                                                      |
|  `400` | Platform not available for archiving.                                                                                                                                                                                         |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                         |
|  `402` | Insufficient credits.                                                                                                                                                                                                         |
|  `403` | The video belongs to another account.                                                                                                                                                                                         |
|  `404` | Video not found.                                                                                                                                                                                                              |
|  `409` | Coverage is inactive, no active account manager is available, the video is not finalized or has no post link, an archive request is already open, or the video is already archived. No credits are charged on these failures. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-request" \
  -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/archive-request', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-request',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-request", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Get / poll the archive request of a video

```http
GET /videos/{id}/archive-request
```

Operation ID: `getVideoArchiveRequest`

Returns the latest archive request for the video, or null. `archived` is true once TokPortal has verified the post is no longer public (status `finalized`).

| Parameter |   In | Required | Description |
| --------- | ---: | -------: | ----------- |
| `id`      | path |      Yes | Video ID.   |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Latest archive request (or null).     |
|  `401` | Missing, invalid, or revoked API key. |
|  `403` | The video belongs to another account. |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-request" \
  -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/archive-request', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-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/videos/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/archive-request", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

## Analytics

### Check analytics refresh availability

```http
GET /accounts/{id}/analytics/can-refresh
```

Operation ID: `canRefreshAccountAnalytics`

Reports whether a manual analytics refresh is currently allowed and when the next one will be. Call it before refreshAccountAnalytics / refreshAnalyticsAccount to avoid a 429 cooldown (6 h for connected accounts, 24 h for the public fallback).

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                                                                                                                                  |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Manual refresh availability. can\_refresh is false and reason identifies the Coverage or analytics-plan blocker when refresh is unavailable. |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Legacy account-analytics refresh path, kept for older integrations; new code should call refreshAnalyticsAccount. Comment collection is gated on three conditions: includeComments only runs when force is true, includePosts is not false, and the workspace holds the full analytics tier. The two you control are refusals, not silence — includeComments without force, or with includePosts false, fails with INVALID\_FIELD for MCP callers and is reported for direct REST callers. The one you cannot control, the analytics tier, comes back on the response as applied.include\_comments plus applied.include\_comments\_dropped\_reason. The call can still return 200 with deduped: true and skippedReason: "recent\_snapshot", which means nothing was refreshed. Check canRefreshAccountAnalytics first: a forced retry consumes the manual-refresh cooldown slot and the next one is rejected with 429. Refresh is blocked for revealed/detached, banned or inactive-Coverage accounts; permanently grandfathered accounts remain eligible.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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.                                                                               |
|  `409` | Account refresh blocked. The response reason identifies the account or TokPortal Coverage state. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"force\": false,\n  \"includePosts\": false,\n  \"includeComments\": false,\n  \"forcePosts\": false\n}")
)
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`

Portfolio-level analytics across your accounts for a date range. Call getAnalyticsContract before interpreting these numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, the two intentionally do not mirror each other, and your access tier may redact metrics — a missing field means unavailable, never zero.

| Parameter   |    In | Required | Description                 |
| ----------- | ----: | -------: | --------------------------- |
| `workspace` | query |       No |                             |
| `platform`  | query |       No | Repeatable platform filter. |
| `country`   | query |       No | Repeatable country filter.  |
| `account`   | query |       No | Repeatable account filter.  |
| `from`      | query |       No |                             |
| `to`        | query |       No |                             |

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/analytics?workspace=example&platform=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/analytics?workspace=example&platform=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/analytics?workspace=example&platform=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?workspace=example&platform=example", 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

CSV export of post-level analytics for the selected accounts and date range. Requires the Creator tier or above. Call getAnalyticsContract before interpreting these numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, the two intentionally do not mirror each other, and your access tier may redact metrics — a missing field means unavailable, never zero.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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 bearer-like access token and URL. The body is validated before anything is rendered, so a malformed request fails with INVALID\_FIELD instead of quietly producing a wrong report for your end client. `from` and `to` are calendar dates in YYYY-MM-DD; a full ISO timestamp is accepted and truncated to its date, and anything else is refused rather than treated as "no filter" (which used to widen the report to the account's entire history). An inverted window (`from` after `to`) is refused too. `template` is a published enum — executive, agency, creator, minimal, growth, board — and an unknown value is refused instead of becoming executive. `brandAccent` must be #rrggbb (#rgb is expanded); named colours, rgb() and 8-digit hex are refused instead of being replaced by the TokPortal default. White-labelling still depends on entitlement: `brandName` and `brandAccent` only apply on the full analytics tier with a Farmer plan, and are otherwise reset. The response carries `applied` — the template, brand name, brand accent and the exact date window the report covers — plus `canBrand`, so read those back instead of inspecting the rendered report. Do not send Idempotency-Key. The successful response contains a secret and is never stored in the replay ledger. A request with the header is rejected with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400) before any ledger claim or operation execution.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CreateAnalyticsReportRequest)

| Status | Description                                                                                                                                                                                                          |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Created analytics report.                                                                                                                                                                                            |
|  `400` | Invalid report request. Idempotency-Key is not supported because the successful response contains a secret. Remove the header before retrying. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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": "executive",
  "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": "executive",
  "brandName": "brandName",
  "brandAccent": "brandAccent"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/analytics/export/reports',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"title\": \"Launch campaign\",\n  \"template\": \"executive\",\n  \"brandName\": \"brandName\",\n  \"brandAccent\": \"brandAccent\"\n}")
)
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\": \"executive\",\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 from the same body as createAnalyticsReport, and validates it identically: `from` and `to` are YYYY-MM-DD (a full ISO timestamp is truncated to its date, anything else is refused with INVALID\_FIELD rather than treated as no filter), an inverted window is refused, `template` is the published enum (executive, agency, creator, minimal, growth, board) and an unknown value is refused instead of becoming executive, and `brandAccent` must be #rrggbb or #rgb. The body of the response is the HTML document itself, so what was applied travels in headers: X-TokPortal-Report-Template, X-TokPortal-Report-Can-Brand, X-TokPortal-Report-Brand-Name, X-TokPortal-Report-From, X-TokPortal-Report-To, alongside X-TokPortal-Export-Rows and X-TokPortal-Export-Accounts. Read those headers to confirm the window and the branding actually used.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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": "executive",
  "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": "executive",
  "brandName": "brandName",
  "brandAccent": "brandAccent"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/analytics/export/reports/html',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"title\": \"Launch campaign\",\n  \"template\": \"executive\",\n  \"brandName\": \"brandName\",\n  \"brandAccent\": \"brandAccent\"\n}")
)
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\": \"executive\",\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`

Daily or weekly time series for one metric, built from stored snapshots and date-scoped rollups — never a live fetch. `mode` selects cumulative totals, per-period gains, or raw snapshot values. Series are excluded on the Starter tier. Call getAnalyticsContract before interpreting these numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, the two intentionally do not mirror each other, and your access tier may redact metrics — a missing field means unavailable, never zero.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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 connected Instagram or TikTok insights

```http
GET /analytics/accounts/{id}/insights
```

Operation ID: `getAnalyticsAccountInsights`

Read stored account-period insights, daily follower/reach history and per-post watch time, completion, traffic and audience data. Requires Creator or above; fields remain tier-aware. available=false and connected\_analytics=null when no recently verified active Zernio connection exists, including after disconnection. Never starts a provider refresh. Account metrics use the explicit period; post metrics are lifetime observations. Missing metrics are omitted, not zero; completion\_rate is 0..1, traffic\_sources and audience\_types are 0..100 percentages, watch times are seconds. Instagram demographics need an eligible audience (normally 100+ followers). Detailed metrics may lag 24–48 hours. The same connected\_analytics block appears on getAnalyticsAccount.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                                               |
| -----: | --------------------------------------------------------- |
|  `200` | Tier-aware connected insights or explicit unavailability. |
|  `401` | Missing, invalid, or revoked API key.                     |
|  `403` | Creator or higher analytics plan required.                |
|  `404` | Account not found or not owned by the API key owner.      |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/insights" \
  -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/insights', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/analytics/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/insights',
    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/insights", 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`

Analytics v2 drilldown for one account: current metrics, top posts and demographics. Includes optional connected\_analytics with account-period insights and post depth only while a Zernio connection has recent active identity proof; absent for disconnected accounts and Starter plans. getAnalyticsAccountInsights returns this block alone. This is the current endpoint; getAccountAnalytics is the older compatibility shape. Call getAnalyticsContract before interpreting numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, and your access tier may redact metrics. Missing fields mean unavailable, never zero.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Refreshes an owner-scoped account through Analytics v2. Comment collection is gated on three conditions: includeComments only runs when force is true, includePosts is not false, and the workspace holds the full analytics tier. The two you control are refusals, not silence — includeComments without force, or with includePosts false, fails with INVALID\_FIELD for MCP callers and is reported for direct REST callers. The one you cannot control, the analytics tier, comes back on the response as applied.include\_comments plus applied.include\_comments\_dropped\_reason. The call can still return 200 with deduped: true and skippedReason: "recent\_snapshot", which means nothing was refreshed. Check canRefreshAccountAnalytics first: a forced retry consumes the manual-refresh cooldown slot and the next one is rejected with 429. Refresh is blocked for revealed/detached, banned or inactive-Coverage accounts; permanently grandfathered accounts remain eligible.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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.                                                                               |
|  `409` | Account refresh blocked. The response reason identifies the account or TokPortal Coverage state. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"force\": false,\n  \"includePosts\": false,\n  \"includeComments\": false,\n  \"forcePosts\": false\n}")
)
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 analytics 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Legacy compatibility view of one account's analytics, kept for older integrations. New code should call getAnalyticsAccount instead. Call getAnalyticsContract before interpreting these numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, the two intentionally do not mirror each other, and your access tier may redact metrics — a missing field means unavailable, never zero.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Post-level analytics for one account, sortable by views, likes, engagement rate or upload date. Values are lifetime per-post totals. Call getAnalyticsContract before interpreting these numbers: top-line values are lifetime per-post totals, connected-account values are rolling windows, the two intentionally do not mirror each other, and your access tier may redact metrics — a missing field means unavailable, never zero.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Lifetime metrics for one tracked post. Interpret them with getAnalyticsContract: these are lifetime per-post totals and deliberately do not match the rolling-window figures shown for a connected account. Also returns `platform_signals` (not recommended, AI label, sound) and `signal_insights` (followers gained, audience split, impression sources, retention curve — Creator tier and above).

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Aggregated comment sentiment and themes across your posts, served from the analytics comment cache — never a live fetch. Full analytics tier only. Filters are not combinable: when `post` is supplied it takes precedence and `platform`, `country`, `account`, `workspace`, `from` and `to` are all ignored, and at most 12 post ids are sampled.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Returns cached comment text for one tracked post of an account. Full analytics tier only; this read never calls the platform live.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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 analytics 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### List posts flagged by the platform

```http
GET /analytics/signals
```

Operation ID: `listAnalyticsSignals`

Owner-scoped list of posted videos carrying a platform signal, newest first: TikTok `not_recommended` (Not For Feed / Not Recommended — the post stays online but is excluded from For You and typically sits at 0 views), `ai_label` (`creator` = declared at posting, `platform` = applied by TikTok moderation) and `sound_status` (`non_commercial` = cannot be promoted, `removed` = pulled for copyright, `muted` = Instagram). Each row carries `platform_signals` (the same object returned on every video by GET /bundles/{id}/videos) and, from the Creator tier, `signal_insights` (followers gained, new vs returning viewers, impression sources, retention curve). Signals are observed by a public check about 48 hours after posting and again after 7 days; `null` means not checked yet, never 'no'. Available on every analytics tier.

| Parameter |    In | Required | Description                                           |
| --------- | ----: | -------: | ----------------------------------------------------- |
| `signal`  | query |       No | Only posts carrying this signal. Omit for any signal. |
| `account` | query |       No | Repeatable saved account filter.                      |
| `from`    | query |       No | Only signals updated after this instant.              |
| `to`      | query |       No | Only signals updated before this instant.             |
| `limit`   | query |       No |                                                       |
| `offset`  | query |       No |                                                       |

| Status | Description                                |
| -----: | ------------------------------------------ |
|  `200` | Flagged posts with their platform signals. |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/analytics/signals?signal=example&account=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/analytics/signals?signal=example&account=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/analytics/signals?signal=example&account=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/signals?signal=example&account=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

## Warming

### Order Advanced Niche Warming (rewarm) on a delivered account

```http
POST /accounts/{id}/rewarm
```

Operation ID: `rewarmAccount`

Starts an Advanced Niche Warming session on a saved account that is already delivered: for each provided term the manager screen-records a session that opens on the account profile (handle visible), searches the term on the account's platform, watches videos from the results, engages with them (likes/saves) and leaves a comment. Every recording is verified before completion. The standard rate is 5 credits per term, charged per target and never per day (3-30 terms, multiples of 3), with a 15-credit minimum; GET /credit-costs returns the effective rate. Once the session has tasks it is NEVER refunded, so confirm the total with the user before calling. Call listAccountWarmingSessions first: only one session can be active per account and a second one is rejected with 409 REWARM\_ALREADY\_ACTIVE. Requires active TokPortal Coverage, a routable active account manager backed by a non-cancelled support order, TikTok or Instagram, and no already-active warming session. A completed bundle remains eligible. Terms are split evenly over 3 calendar days in the manager's timezone; earlier-day tasks remain available until completed and sessions with tasks do not expire.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Saved account ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (RewarmAccountRequest)

| Status | Description                                                                                                                                                                                                                                  |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Warming session created and started.                                                                                                                                                                                                         |
|  `400` | Invalid terms (ADVANCED\_WARMING\_TERMS) or unsupported platform (ADVANCED\_WARMING\_PLATFORM).                                                                                                                                              |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                        |
|  `402` | Insufficient credits.                                                                                                                                                                                                                        |
|  `403` | Account belongs to another user.                                                                                                                                                                                                             |
|  `404` | Account not found.                                                                                                                                                                                                                           |
|  `409` | No routable active account manager or non-cancelled support order (legacy code REWARM\_NO\_ACTIVE\_ORDER), Coverage is inactive, or a warming session is already active (REWARM\_ALREADY\_ACTIVE). No credits are charged on these failures. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/rewarm" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "search_terms": [
    "search_terms 1",
    "search_terms 2",
    "search_terms 3"
  ]
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/rewarm', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "search_terms": [
    "search_terms 1",
    "search_terms 2",
    "search_terms 3"
  ]
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/rewarm',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"search_terms\": [\n    \"search_terms 1\",\n    \"search_terms 2\",\n    \"search_terms 3\"\n  ]\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/rewarm", strings.NewReader("{\n  \"search_terms\": [\n    \"search_terms 1\",\n    \"search_terms 2\",\n    \"search_terms 3\"\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)
```

### Configure the niche targets of a count-only Advanced Niche Warming purchase

```http
PUT /bundles/{id}/warming-terms
```

Operation ID: `configureBundleWarmingTerms`

Only for a bundle created with advanced\_warming\_terms\_count (deferred targets). To warm an account that is already delivered, use rewarmAccount instead; this endpoint answers 404 WARMING\_SESSION\_NOT\_FOUND when there is no count-only purchase to configure. Must provide EXACTLY the purchased number of targets. One-shot: once configured the targets cannot be changed. Unlike PUT /bundles/{id}/account, this stays available at any account status after the manager accepted the mission or submitted the account, in which case the warming session starts immediately. Requires active TokPortal Coverage on the resolved saved account, or permanent grandfathering: a due period renews automatically at the account's stored rate immediately before execution, and nothing starts if it cannot renew. See getAccountManagedSubscription. A new account that has not produced a saved account yet remains configurable before delivery.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Bundle ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (ConfigureWarmingTermsRequest)

| Status | Description                                                                                                                                                                                                                                                                                       |
| -----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Targets configured (started=true when the session began immediately).                                                                                                                                                                                                                             |
|  `400` | Wrong number of targets or invalid targets (ADVANCED\_WARMING\_TERMS), or an unrecognised field in the body (UNKNOWN\_FIELD).                                                                                                                                                                     |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                                                                                                                                                             |
|  `404` | Bundle not found, or no warming session to configure (WARMING\_SESSION\_NOT\_FOUND).                                                                                                                                                                                                              |
|  `409` | Targets already configured (WARMING\_TERMS\_ALREADY\_SET). If error.code is MANAGED\_ACCOUNT\_TASK\_BLOCKED, read error.details.account\_id and error.details.reason, fetch GET /accounts/{account\_id}/managed-subscription, and reactivate only when that state is recoverable before retrying. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X PUT "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-terms" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "advanced_warming_terms": [
    "advanced_warming_terms 1",
    "advanced_warming_terms 2",
    "advanced_warming_terms 3"
  ]
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-terms', {
  method: 'PUT',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "advanced_warming_terms": [
    "advanced_warming_terms 1",
    "advanced_warming_terms 2",
    "advanced_warming_terms 3"
  ]
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PUT',
    'https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-terms',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"advanced_warming_terms\": [\n    \"advanced_warming_terms 1\",\n    \"advanced_warming_terms 2\",\n    \"advanced_warming_terms 3\"\n  ]\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PUT", "https://app.tokportal.com/api/ext/bundles/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-terms", strings.NewReader("{\n  \"advanced_warming_terms\": [\n    \"advanced_warming_terms 1\",\n    \"advanced_warming_terms 2\",\n    \"advanced_warming_terms 3\"\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)
```

### List Advanced Niche Warming sessions for an account

```http
GET /accounts/{id}/warming-sessions
```

Operation ID: `listAccountWarmingSessions`

Lists Advanced Niche Warming sessions (newest first) for a saved account you own, including per-term tasks, per-term reports, proof links, and the aggregated session report when completed. Tasks unlock over 3 calendar days in the manager's timezone and, once created, do not expire.

| Parameter |   In | Required | Description       |
| --------- | ---: | -------: | ----------------- |
| `id`      | path |      Yes | Saved account ID. |

| Status | Description                              |
| -----: | ---------------------------------------- |
|  `200` | Warming sessions with tasks and reports. |
|  `401` | Missing, invalid, or revoked API key.    |
|  `403` | Account belongs to another user.         |
|  `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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-sessions" \
  -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/warming-sessions', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/accounts/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/warming-sessions',
    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/warming-sessions", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Get an Advanced Niche Warming session

```http
GET /warming-sessions/{id}
```

Operation ID: `getWarmingSession`

Returns one Advanced Niche Warming session you own: status, per-term tasks (with 3-calendar-day dispatch in the manager's timezone, verification status, client report and proof video link), and the aggregated report on completion. Created tasks remain open until completed; they do not expire.

| Parameter |   In | Required | Description         |
| --------- | ---: | -------: | ------------------- |
| `id`      | path |      Yes | Warming session ID. |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Warming session detail.               |
|  `401` | Missing, invalid, or revoked API key. |
|  `404` | Warming session 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/warming-sessions/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/warming-sessions/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/warming-sessions/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/warming-sessions/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Suggest warming search terms from a niche description (free)

```http
POST /warming/generate-terms
```

Operation ID: `generateWarmingTerms`

Generates platform-appropriate Advanced Niche Warming search terms (niche targets) from a free-text niche description (max 1000 characters). This is free and no credits are charged. Call it before buying warming rather than inventing terms: pass the result to advanced\_warming\_terms (bundle creation or PUT /bundles/{id}/warming-terms deferred configuration) or to search\_terms (rewarmAccount).

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (GenerateWarmingTermsRequest)

| Status | Description                                                                                                 |
| -----: | ----------------------------------------------------------------------------------------------------------- |
|  `200` | Generated search terms.                                                                                     |
|  `400` | Invalid count/description (ADVANCED\_WARMING\_TERMS) or unsupported platform (ADVANCED\_WARMING\_PLATFORM). |
|  `401` | Missing, invalid, or revoked API key.                                                                       |
|  `502` | Term generation failed (TERM\_GENERATION\_FAILED).                                                          |
|  `503` | AI generation unavailable (AI\_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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/warming/generate-terms" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "text",
  "platform": "tiktok",
  "count": 27
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/warming/generate-terms', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "text": "text",
  "platform": "tiktok",
  "count": 27
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/warming/generate-terms',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"text\": \"text\",\n  \"platform\": \"tiktok\",\n  \"count\": 27\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/warming/generate-terms", strings.NewReader("{\n  \"text\": \"text\",\n  \"platform\": \"tiktok\",\n  \"count\": 27\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)
```

## Webhooks

### List webhook event catalog

```http
GET /webhooks/events
```

Operation ID: `listWebhookEvents`

Returns the supported webhook event types with, for each one, its description, exact trigger condition, payload schema and an example payload, plus the delivery envelope and signature scheme. Read it before subscribing: several names are counter-intuitive — account.published fires when you publish the order, not when the account is delivered (that is account.in\_review), and warming.session\_started is not emitted for warming whose terms were set at bundle creation. 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


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`

Lists your webhook endpoints with their subscribed events and enabled state. Signing secrets are never returned here — only once, at creation.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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 it, because a lost secret can only be replaced by deleting the endpoint and creating a new one. Call listWebhookEvents before choosing `events`: it returns the description, trigger condition and payload schema of each event, and several names are counter-intuitive. In particular account.published fires when YOU publish the order, not when the account is delivered — the delivery signal is account.in\_review, and account.finalized fires when the account review is approved. Do not send Idempotency-Key. The successful response contains a secret and is never stored in the replay ledger. A request with the header is rejected with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400) before any ledger claim or operation execution.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CreateWebhookEndpointRequest)

| Status | Description                                                                                                                                                                                                            |
| -----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `201` | Webhook endpoint created.                                                                                                                                                                                              |
|  `400` | Invalid webhook endpoint. Idempotency-Key is not supported because the successful response contains a secret. Remove the header before retrying. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/webhooks',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"url\": \"https://example.com/resource\",\n  \"events\": [\n    \"webhook.test\"\n  ]\n}")
)
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`

Returns one webhook endpoint's URL, subscribed events and enabled state. The signing secret is not returned; if you lost it, delete this endpoint and create a replacement.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Changes an endpoint's URL, subscribed events, description or enabled state. The signing secret is unchanged and is never re-returned. Call listWebhookEvents for the meaning, trigger condition and payload of each event type before changing the subscription list.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Webhook endpoint ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"url\": \"https://example.com/resource\",\n  \"events\": [\n    \"webhook.test\"\n  ],\n  \"description\": \"Campaign content description.\",\n  \"enabled\": false\n}")
)
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`

Permanently deletes a webhook endpoint and invalidates its signing secret. Events that could not be delivered are not queued for a replacement endpoint.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Webhook endpoint ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Lists delivery attempts for one endpoint with the response status and body. Deliveries are at-least-once and retries reuse the same `TokPortal-Event-Id` — deduplicate on that id, not on the delivery id.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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

### Get a link to upload a video file

```http
POST /upload/video
```

Operation ID: `uploadVideo`

Returns a short-lived presigned upload capability for a video file. The `public_url` it returns is what goes into `video_url` on configureBundleVideo — images are the opposite and require the upload response's `storage_path`. Do not send Idempotency-Key. The successful response contains a secret and is never stored in the replay ledger. A request with the header is rejected with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400) before any ledger claim or operation execution.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (UploadVideoRequest)

| Status | Description                                                                                                                                                                                                          |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Presigned video upload URL.                                                                                                                                                                                          |
|  `400` | Invalid upload request. Idempotency-Key is not supported because the successful response contains a secret. Remove the header before retrying. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/upload/video',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"filename\": \"media.mp4\",\n  \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
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

```http
POST /upload/video/direct
```

Operation ID: `uploadVideoDirect`

Uploads multipart/form-data directly through TokPortal and returns the public video URL, which goes straight into `video_url` on configureBundleVideo.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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

### Get a link to upload an image

```http
POST /upload/image
```

Operation ID: `uploadImage`

Returns a short-lived signed upload URL and upload token for an image. Use the returned `storage_path` — NOT `public_url` — for `carousel_images` and `profile_picture_url`; videos are the opposite, uploadVideo returns a `public_url` that goes straight into `video_url`. The stored object's extension comes from the declared `content_type` (image/png stores a .png), and only falls back to the extension in `filename` when the content type is unknown, so send the content type that matches the bytes you are about to PUT. `storage_path` on the response is the key the object is actually stored under — read it rather than rebuilding it from `filename`. Do not send Idempotency-Key. The successful response contains a secret and is never stored in the replay ledger. A request with the header is rejected with IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE (400) before any ledger claim or operation execution.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (UploadImageRequest)

| Status | Description                                                                                                                                                                                                          |
| -----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  `200` | Signed image upload URL.                                                                                                                                                                                             |
|  `400` | Invalid upload request. Idempotency-Key is not supported because the successful response contains a secret. Remove the header before retrying. Error code: IDEMPOTENCY\_KEY\_NOT\_ALLOWED\_FOR\_SENSITIVE\_RESPONSE. |
|  `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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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": "profile.png",
  "content_type": "image/png",
  "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": "profile.png",
  "content_type": "image/png",
  "bundle_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/upload/image',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"filename\": \"profile.png\",\n  \"content_type\": \"image/png\",\n  \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/upload/image", strings.NewReader("{\n  \"filename\": \"profile.png\",\n  \"content_type\": \"image/png\",\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. Use the returned `storage_path` — NOT `public_url` — for `carousel_images` and `profile_picture_url`. HEIF/HEIC may be converted to JPEG. `purpose` is validated like it is on the JSON upload endpoints: an explicit value other than `carousel` or `profile_picture` is refused with INVALID\_FIELD for MCP callers and reported in `meta.ignored_fields` for direct REST callers — it is no longer quietly rounded down to `carousel`. Omitting `purpose` still means `carousel`, which is the documented default. Confirm the destination with the returned `bucket`.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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 a public URL

```http
POST /upload/image/from-url
```

Operation ID: `uploadImageFromUrl`

Fetches a public direct image URL and stores it permanently in TokPortal storage. Use the returned `storage_path` — NOT `public_url` — for `carousel_images` and `profile_picture_url`.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/upload/image/from-url',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"url\": \"https://example.com/resource\",\n  \"bundle_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
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`

Lists owned comment tasks. execution\_blocked and execution\_block\_reason identify tasks paused by inactive TokPortal Coverage; reads remain available while execution is paused.

| Parameter          |    In | Required | Description                                                 |
| ------------------ | ----: | -------: | ----------------------------------------------------------- |
| `page`             | query |       No | Page number.                                                |
| `per_page`         | query |       No | Items per page.                                             |
| `status`           | query |       No | Filter by one status or a comma-separated list of statuses. |
| `saved_account_id` | query |       No | Filter by a user-owned saved account ID.                    |

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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`

Creates one comment task or a partial-success batch of up to 200. Each accepted task costs 1 credit. The target is any public TikTok or Instagram video, including a third party's — that is the intended use — while the posting account must be one of your own delivered accounts and its platform must match the target's (COMMENT\_PLATFORM\_MISMATCH otherwise). Deduplicate locally on (saved\_account\_id, target URL, comment\_text): two different Idempotency-Keys carrying the same comment bill twice. The saved account must be client-owned, manager-assigned, and allowed to execute tasks by TokPortal Coverage, unless it is permanently grandfathered. TokPortal locks and revalidates the account and Coverage before the accepted-task debit and all accepted inserts commit in one transaction. Rejected rows are never charged. Read credits\_charged instead of calculating it from the request, and read meta.rejected\_count and each row's reason rather than inferring success from the 201. Send an Idempotency-Key and reuse the same key, method, path, and body after an uncertain transport result so the completed response is replayed without a second charge.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key`     | header |       No | Strongly recommended for this credit or workflow mutation. Use one unique key per logical request and reuse the same key only with the same method, path, query, and body after an uncertain result. Maximum 255 characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

Request body: `application/json` (CreateCommentTaskRequest)

| Status | Description                                                                                                                                            |
| -----: | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|  `201` | Created and rejected comment-task rows.                                                                                                                |
|  `400` | Invalid comment task request.                                                                                                                          |
|  `401` | Missing, invalid, or revoked API key.                                                                                                                  |
|  `402` | Insufficient credits.                                                                                                                                  |
|  `409` | Single task blocked by account state or inactive TokPortal Coverage. For a batch, inspect each rejected row instead.                                   |
|  `500` | The batch outcome could not be confirmed. Retry the exact request with the same Idempotency-Key; do not create a new logical request until reconciled. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### Examples

**curl**

```bash
curl -X POST "https://app.tokportal.com/api/ext/comments" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Idempotency-Key: createCommentTasks-example-1" \
  -H "Content-Type: application/json" \
  -d '{
  "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "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',
    'Idempotency-Key': 'createCommentTasks-example-1',
  },
  body: JSON.stringify({
  "saved_account_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "comment_text": "comment_text"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/comments',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"], "Idempotency-Key": "createCommentTasks-example-1"},
    json=json.loads("{\n  \"saved_account_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"comment_text\": \"comment_text\"\n}")
)
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  \"comment_text\": \"comment_text\"\n}"))
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
req.Header.Set("Idempotency-Key", "createCommentTasks-example-1")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
```

### Get a comment task

```http
GET /comments/{id}
```

Operation ID: `getCommentTask`

Returns one owned comment task, including whether inactive TokPortal Coverage currently blocks manager execution and the stable block reason.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

### Cancel a pending comment task

```http
DELETE /comments/{id}
```

Operation ID: `deleteCommentTask`

Cancels an owned task only while its status is pending. The terminal transition and refund of the task's exact stored credit cost commit atomically and are idempotent. Current tasks refund 1 credit; credits\_refunded remains authoritative for historical tasks. Cancellation remains available when Coverage is paused because it stops pending work rather than executing it. Reuse the same Idempotency-Key after an uncertain transport result.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Comment task ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `Idempotency-Key`     | header |       No | Strongly recommended for this credit or workflow mutation. Use one unique key per logical request and reuse the same key only with the same method, path, query, and body after an uncertain result. Maximum 255 characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| Status | Description                           |
| -----: | ------------------------------------- |
|  `200` | Cancellation and exact refund result. |
|  `401` | Missing, invalid, or revoked API key. |
|  `404` | Comment task not found.               |
|  `409` | Only a pending task can be cancelled. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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" \
  -H "Idempotency-Key: deleteCommentTask-example-1"
```

**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!,
    'Idempotency-Key': 'deleteCommentTask-example-1',
  }
});
const data = await response.json();
```

**Python**

```python


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"], "Idempotency-Key": "deleteCommentTask-example-1"}
)
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"))
req.Header.Set("Idempotency-Key", "deleteCommentTask-example-1")
resp, err := http.DefaultClient.Do(req)
```

### Approve a manually confirmed comment task

```http
POST /comments/{id}/approve
```

Operation ID: `approveCommentTask`

Approves and finalizes an owned manually\_confirmed task. Execution is blocked while TokPortal Coverage is inactive; inspect MANAGED\_ACCOUNT\_TASK\_BLOCKED and reactivate a recoverable account first.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Comment task ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

| 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, or TokPortal Coverage currently blocks execution. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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`

Returns an owned manually\_confirmed task to pending corrections. Execution is blocked while TokPortal Coverage is inactive; inspect MANAGED\_ACCOUNT\_TASK\_BLOCKED and reactivate a recoverable account first.

| Parameter             |     In | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------- | -----: | -------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  |   path |      Yes | Comment task ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `X-TokPortal-Dry-Run` | header |       No | Send `true` (or `1`/`yes`) to SIMULATE this call instead of executing it. A dry run performs the full validation and pricing path of the real operation and stops before the first write: no credits are debited, no rows are created, no webhooks fire, and no operator ever sees it. The response has the same shape and status as the real call and adds `dry_run: true`, `dry_run_notice`, and — on credit-spending operations — `credits_would_charge` carrying the real price. Errors are identical to the real call, which is the point: this is how you learn what an operation will do before paying for it. Rate limits are consumed normally, a supplied Idempotency-Key is ignored rather than burned, and identifiers returned by a dry run are synthetic (see DRY\_RUN\_ID\_IN\_LIVE\_REQUEST). |

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, or TokPortal Coverage currently blocks execution. |

#### 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 at which the current one-minute budget window rolls over.                                                                                                         |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.                                                                                  |
| `Idempotent-Replayed`       | Present with value true when a mutating request is replayed from a completed Idempotency-Key.                                                                                    |
| `X-TokPortal-Dry-Run`       | Present and set to `true` when the request was simulated. This is the only dry-run marker on error responses, so a client can confirm it spent nothing without parsing the body. |

#### 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


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=json.loads("{\n  \"reason\": \"Please review this item.\"\n}")
)
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`

Lists verifier attempts for one owned task. This read remains available while TokPortal Coverage pauses task execution.

| 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### 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


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

## AI

### Read AI availability without starting provider work

```http
GET /ai/status
```

Operation ID: `getAiStatus`

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/status" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/status', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/status',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/ai/status", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Discover AI models and capabilities

```http
GET /ai/models
```

Operation ID: `aiCatalog`

Lists public model aliases and availability. A blocked capability is not usable. Fetch the chosen model schema before quoting; never infer support from a model name.

| Parameter    |    In | Required | Description |
| ------------ | ----: | -------: | ----------- |
| `capability` | query |       No |             |
| `cursor`     | query |       No |             |
| `limit`      | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/models?capability=example&cursor=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/models?capability=example&cursor=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/models?capability=example&cursor=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/ai/models?capability=example&cursor=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read a model's current input schema and capabilities

```http
GET /ai/models/{model_id}
```

Operation ID: `aiModelSchema`

| Parameter   |    In | Required | Description |
| ----------- | ----: | -------: | ----------- |
| `model_id`  |  path |      Yes |             |
| `operation` | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/models/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c?operation=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/models/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c?operation=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/models/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c?operation=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/ai/models/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c?operation=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Quote an AI operation in USD before spending

```http
POST /ai/quotes
```

Operation ID: `aiQuote`

Free preflight; never starts paid provider work. Freezes model, operation and inputs into a workspace-bound quote valid for 15 minutes. Read availability and the maximum USD price, get user approval, then generate with this quote ID. The AI dollar wallet is separate from TokPortal management credits.

Request body: `application/json` (AiQuoteRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Immutable quote created; no paid provider work started.                                                  |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/quotes" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "model"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/quotes', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "model": "model"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/quotes',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"model\": \"model\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/quotes", strings.NewReader("{\n  \"model\": \"model\"\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)
```

### Generate media with automatic AI wallet billing

```http
POST /ai/generations
```

Operation ID: `aiGenerate`

Send model, prompt and model-specific controls directly. The server prices and reserves funds automatically within the key's approved per-job and monthly limits. Optional max\_cost\_usd sets a lower request cap. A separate quote is not required; quote\_id plus max\_cost\_usd remains supported for explicit cost previews. Requires AI spending permission, wallet balance and Idempotency-Key. Reuse the same key and body after uncertainty. Does not publish. Poll with ai\_inspect resource jobs. For video drafts, propose 480p (lower cost), 720p for a balance, or 1080p for final quality; respect the user's choice.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiGenerationRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `202` | Accepted as a durable TokPortal job. Poll its local ID; do not resubmit.                                 |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/generations" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "model"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/generations', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "model": "model"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/generations',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"model\": \"model\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/generations", strings.NewReader("{\n  \"model\": \"model\"\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 AI jobs

```http
GET /ai/jobs
```

Operation ID: `listAiJobs`

| Parameter |    In | Required | Description |
| --------- | ----: | -------: | ----------- |
| `cursor`  | query |       No |             |
| `limit`   | query |       No |             |
| `status`  | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/jobs?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/jobs?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/jobs?cursor=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/ai/jobs?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read an AI job

```http
GET /ai/jobs/{job_id}
```

Operation ID: `getAiJob`

| Parameter |   In | Required | Description |
| --------- | ---: | -------: | ----------- |
| `job_id`  | path |      Yes |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/jobs/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/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/jobs/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/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Request cancellation of an AI job

```http
POST /ai/jobs/{job_id}/cancel
```

Operation ID: `cancelAiJob`

Cancellation is best effort. Accepted provider work may remain billable; cancellation is not a refund promise.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `job_id`          |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/cancel" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/cancel', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/cancel',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/jobs/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/cancel", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### List reusable AI media assets

```http
GET /ai/assets
```

Operation ID: `listAiAssets`

| Parameter |    In | Required | Description |
| --------- | ----: | -------: | ----------- |
| `cursor`  | query |       No |             |
| `limit`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/assets?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/assets?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/assets?cursor=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/ai/assets?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Import a media URL into the AI asset library

```http
POST /ai/assets/import
```

Operation ID: `importAiAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiAssetImportRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/assets/import" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/resource"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/assets/import', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "url": "https://example.com/resource"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/assets/import',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"url\": \"https://example.com/resource\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/assets/import", strings.NewReader("{\n  \"url\": \"https://example.com/resource\"\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 a signed media upload

```http
POST /ai/assets/uploads
```

Operation ID: `createAiUpload`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiUploadRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/assets/uploads" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "filename": "media.mp4",
  "content_type": "video/mp4",
  "size_bytes": 25
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/assets/uploads', {
  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",
  "size_bytes": 25
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/assets/uploads',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"filename\": \"media.mp4\",\n  \"content_type\": \"video/mp4\",\n  \"size_bytes\": 25\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/assets/uploads", strings.NewReader("{\n  \"filename\": \"media.mp4\",\n  \"content_type\": \"video/mp4\",\n  \"size_bytes\": 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)
```

### Verify and register a completed media upload

```http
POST /ai/assets/uploads/complete
```

Operation ID: `completeAiUpload`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiUploadCompleteRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/assets/uploads/complete" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "upload_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/assets/uploads/complete', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "upload_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/assets/uploads/complete',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"upload_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/assets/uploads/complete", strings.NewReader("{\n  \"upload_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)
```

### Read an AI media asset

```http
GET /ai/assets/{asset_id}
```

Operation ID: `getAiAsset`

| Parameter  |   In | Required | Description |
| ---------- | ---: | -------: | ----------- |
| `asset_id` | path |      Yes |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/assets/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/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/assets/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/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Update AI asset metadata

```http
PATCH /ai/assets/{asset_id}
```

Operation ID: `updateAiAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `asset_id`        |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiAssetUpdateRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "name",
  "metadata": {}
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "name": "name",
  "metadata": {}
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"name\": \"name\",\n  \"metadata\": {}\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", strings.NewReader("{\n  \"name\": \"name\",\n  \"metadata\": {}\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 an AI asset

```http
DELETE /ai/assets/{asset_id}
```

Operation ID: `deleteAiAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `asset_id`        |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/assets/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/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'DELETE',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'DELETE',
    'https://app.tokportal.com/api/ext/ai/assets/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/ai/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### List AI asset collections

```http
GET /ai/collections
```

Operation ID: `listAiCollections`

| Parameter |    In | Required | Description |
| --------- | ----: | -------: | ----------- |
| `cursor`  | query |       No |             |
| `limit`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/collections?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/collections?cursor=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/ai/collections?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Create an AI asset collection

```http
POST /ai/collections
```

Operation ID: `createAiCollection`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiCollectionRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "name"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "name": "name"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/collections',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"name\": \"name\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/collections", strings.NewReader("{\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)
```

### Read an AI asset collection

```http
GET /ai/collections/{collection_id}
```

Operation ID: `getAiCollection`

| Parameter       |   In | Required | Description |
| --------------- | ---: | -------: | ----------- |
| `collection_id` | path |      Yes |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/collections/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/collections/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Update an AI asset collection

```http
PATCH /ai/collections/{collection_id}
```

Operation ID: `updateAiCollection`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiCollectionRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "name"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "name": "name"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"name\": \"name\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", strings.NewReader("{\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)
```

### Delete an AI asset collection

```http
DELETE /ai/collections/{collection_id}
```

Operation ID: `deleteAiCollection`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'DELETE',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'DELETE',
    'https://app.tokportal.com/api/ext/ai/collections/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### List every asset reference in an owned collection

```http
GET /ai/collections/{collection_id}/assets
```

Operation ID: `listAiCollectionAssets`

| Parameter       |    In | Required | Description |
| --------------- | ----: | -------: | ----------- |
| `collection_id` |  path |      Yes |             |
| `cursor`        | query |       No |             |
| `limit`         | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets?cursor=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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Add an asset to a collection

```http
POST /ai/collections/{collection_id}/assets
```

Operation ID: `addAiCollectionAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (inline schema)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "asset_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "asset_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"asset_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets", strings.NewReader("{\n  \"asset_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)
```

### Remove an asset from a collection

```http
DELETE /ai/collections/{collection_id}/assets/{asset_id}
```

Operation ID: `removeAiCollectionAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `asset_id`        |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'DELETE',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'DELETE',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Rename a collection asset reference

```http
PATCH /ai/collections/{collection_id}/assets/{asset_id}
```

Operation ID: `updateAiCollectionAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `asset_id`        |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (inline schema)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "name"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "name": "name"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"name\": \"name\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", strings.NewReader("{\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)
```

### Refresh a collection asset reference processing status

```http
POST /ai/collections/{collection_id}/assets/{asset_id}/refresh
```

Operation ID: `refreshAiCollectionAsset`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_id`   |   path |      Yes |                                                                                                                                               |
| `asset_id`        |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/collections/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/assets/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Browse the available reusable reference library

```http
GET /ai/library
```

Operation ID: `listAiLibrary`

| Parameter     |    In | Required | Description |
| ------------- | ----: | -------: | ----------- |
| `query`       | query |       No |             |
| `page_number` | query |       No |             |
| `page_size`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/library?query=example&page_number=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/library?query=example&page_number=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/library?query=example&page_number=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/ai/library?query=example&page_number=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read an authorized reference thumbnail

```http
GET /ai/library/{reference_id}/thumbnail
```

Operation ID: `getAiLibraryThumbnail`

| Parameter      |   In | Required | Description |
| -------------- | ---: | -------: | ----------- |
| `reference_id` | path |      Yes |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Private thumbnail bytes; authenticate with the same TokPortal key.                                       |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/library/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/thumbnail" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/library/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/thumbnail', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/library/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/thumbnail',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/ai/library/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/thumbnail", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### List human-verification sessions

```http
GET /ai/verifications
```

Operation ID: `listAiVerifications`

| Parameter |    In | Required | Description |
| --------- | ----: | -------: | ----------- |
| `cursor`  | query |       No |             |
| `limit`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/verifications?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/verifications?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/verifications?cursor=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/ai/verifications?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Create an explicitly authorized human-verification session

```http
POST /ai/verifications
```

Operation ID: `createAiVerification`

Starts an external human-verification flow, not a generated model job. Availability requires explicit operational approval of provider pricing. No claim that this is free. The returned link expires quickly and permits a single verification attempt.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiVerificationRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/verifications" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "name",
  "consent": true
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/verifications', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "name": "name",
  "consent": true
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/verifications',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"name\": \"name\",\n  \"consent\": true\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/verifications", strings.NewReader("{\n  \"name\": \"name\",\n  \"consent\": true\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)
```

### Read a locally persisted human-verification session

```http
GET /ai/verifications/{verification_id}
```

Operation ID: `getAiVerification`

| Parameter         |   In | Required | Description |
| ----------------- | ---: | -------: | ----------- |
| `verification_id` | path |      Yes |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/verifications/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/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/verifications/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/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Explicitly refresh a human-verification result

```http
POST /ai/verifications/{verification_id}/refresh
```

Operation ID: `refreshAiVerification`

Queries the provider using the server-held verification token. Never auto-retry a one-attempt verification. Use the same idempotency key after an uncertain result.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `verification_id` |   path |      Yes |                                                                                                                                               |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/verifications/9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c/refresh", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Run a quoted text operation with optional streaming

```http
POST /ai/chat/completions
```

Operation ID: `createAiChatCompletion`

Uses the quote's frozen prompt/options, not a second mutable prompt. stream=false returns JSON; stream=true returns TokPortal Server-Sent Events for job lifecycle and final results, not an upstream token-by-token compatibility stream. A client disconnect is not proof the provider cancelled. Reconcile the job/request before retrying. MCP uses ai\_generate for a pollable job instead.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiTextRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | TokPortal job lifecycle and result events (stream=true)                                                  |
|  `202` | Durable text job accepted (stream=false).                                                                |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/chat/completions" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "quote_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "max_cost_usd": "2.500000"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/chat/completions', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "quote_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "max_cost_usd": "2.500000"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/chat/completions',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"quote_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"max_cost_usd\": \"2.500000\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/chat/completions", strings.NewReader("{\n  \"quote_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"max_cost_usd\": \"2.500000\"\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)
```

### Run a quoted text operation with optional streaming

```http
POST /ai/responses
```

Operation ID: `createAiResponse`

Uses the quote's frozen prompt/options, not a second mutable prompt. stream=false returns JSON; stream=true returns TokPortal Server-Sent Events for job lifecycle and final results, not an upstream token-by-token compatibility stream. A client disconnect is not proof the provider cancelled. Reconcile the job/request before retrying. MCP uses ai\_generate for a pollable job instead.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiTextRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | TokPortal job lifecycle and result events (stream=true)                                                  |
|  `202` | Durable text job accepted (stream=false).                                                                |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/responses" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "quote_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "max_cost_usd": "2.500000"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/responses', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "quote_id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c",
  "max_cost_usd": "2.500000"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/responses',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"quote_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"max_cost_usd\": \"2.500000\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/responses", strings.NewReader("{\n  \"quote_id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\",\n  \"max_cost_usd\": \"2.500000\"\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)
```

### Read the separate AI USD wallet

```http
GET /ai/wallet
```

Operation ID: `getAiWallet`

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/wallet" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/wallet', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/wallet',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/ai/wallet", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Read AI USD wallet transactions

```http
GET /ai/wallet/transactions
```

Operation ID: `listAiWalletTransactions`

| Parameter |    In | Required | Description |
| --------- | ----: | -------: | ----------- |
| `cursor`  | query |       No |             |
| `limit`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/wallet/transactions?cursor=example&limit=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/wallet/transactions?cursor=example&limit=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/wallet/transactions?cursor=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/ai/wallet/transactions?cursor=example&limit=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Create an explicitly approved AI wallet top-up checkout

```http
POST /ai/wallet/topups
```

Operation ID: `aiTopup`

Billing action, never an implied prerequisite to generation. Only call after the user explicitly approves the displayed USD amount. Return the payment URL for the user to complete; never claim the wallet was credited merely because checkout was created.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiTopupRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `201` | Checkout created; this does not confirm a payment or wallet credit.                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/wallet/topups" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "amount_usd": "2.500000",
  "billing_consent": true
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/wallet/topups', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "amount_usd": "2.500000",
  "billing_consent": true
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/wallet/topups',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"amount_usd\": \"2.500000\",\n  \"billing_consent\": true\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/wallet/topups", strings.NewReader("{\n  \"amount_usd\": \"2.500000\",\n  \"billing_consent\": true\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)
```

### Read AI automatic top-up settings

```http
GET /ai/wallet/auto-topup
```

Operation ID: `getAiAutoTopup`

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/wallet/auto-topup" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/wallet/auto-topup', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/wallet/auto-topup',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/ai/wallet/auto-topup", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Configure recurring AI wallet top-up authority

```http
PATCH /ai/wallet/auto-topup
```

Operation ID: `updateAiAutoTopup`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiAutoTopupRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/wallet/auto-topup" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/wallet/auto-topup', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "enabled": false
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/wallet/auto-topup',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"enabled\": false\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/wallet/auto-topup", strings.NewReader("{\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)
```

### Read this key's AI permissions and budgets

```http
GET /ai/access
```

Operation ID: `getAiAccess`

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/access" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/access', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/access',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]}
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://app.tokportal.com/api/ext/ai/access", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Configure explicitly approved AI key access and budgets

```http
PATCH /ai/access
```

Operation ID: `updateAiAccess`

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiAccessRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/access" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/access', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "enabled": false
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/access',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"enabled\": false\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/access", strings.NewReader("{\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)
```

### Inspect AI jobs, media, wallet or permissions

```http
GET /ai/inspect
```

Operation ID: `aiInspect`

Read-only facade over fixed AI resources. Optional id reads one owned job, asset, collection, or verification; omit it to list. wallet, transactions, access, auto\_topup and status do not accept a resource ID. No provider work or payment is started.

| Parameter     |    In | Required | Description |
| ------------- | ----: | -------: | ----------- |
| `resource`    | query |      Yes |             |
| `id`          | query |       No |             |
| `cursor`      | query |       No |             |
| `limit`       | query |       No |             |
| `query`       | query |       No |             |
| `page_number` | query |       No |             |
| `page_size`   | query |       No |             |

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |

#### Examples

**curl**

```bash
curl -X GET "https://app.tokportal.com/api/ext/ai/inspect?resource=example&id=example" \
  -H "X-API-Key: sk_your_key_here"
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/inspect?resource=example&id=example', {
  method: 'GET',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,

  }
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'GET',
    'https://app.tokportal.com/api/ext/ai/inspect?resource=example&id=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/ai/inspect?resource=example&id=example", nil)
req.Header.Set("X-API-Key", os.Getenv("TOKPORTAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
```

### Manage AI media, collections, cancellations and human verification

```http
POST /ai/manage
```

Operation ID: `aiManage`

A fixed action enum, never an arbitrary HTTP proxy. Review the action before approving: deletion can remove media, cancellation does not guarantee a refund, and verification starts or refreshes an explicitly approved external human-verification session. All actions require Idempotency-Key. Verification is operationally gated until its provider pricing and consent requirements are approved.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiManageRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/manage" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "action": "cancel_job",
  "id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/manage', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "action": "cancel_job",
  "id": "9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c"
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'POST',
    'https://app.tokportal.com/api/ext/ai/manage',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"action\": \"cancel_job\",\n  \"id\": \"9f3a7b2e-1c4d-4e8f-a5b6-7d9e0f1a2b3c\"\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("POST", "https://app.tokportal.com/api/ext/ai/manage", strings.NewReader("{\n  \"action\": \"cancel_job\",\n  \"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)
```

### Configure AI permissions, budgets or automatic billing

```http
PATCH /ai/configure
```

Operation ID: `aiConfigure`

Changes future spending authority. resource selects access or auto\_topup. Require explicit user approval for enabling generation/billing or raising limits. Enabling automatic top-ups requires billing\_consent:true. Never silently grant a key permissions or turn on recurring payments to make another operation succeed.

| Parameter         |     In | Required | Description                                                                                                                                   |
| ----------------- | -----: | -------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | header |      Yes | Required for a logical spending/payment request. Reuse the same key and identical body after an uncertain result; never retry with a new key. |

Request body: `application/json` (AiConfigureRequest)

| Status | Description                                                                                              |
| -----: | -------------------------------------------------------------------------------------------------------- |
|  `200` | Successful response                                                                                      |
|  `400` | Invalid input, missing idempotency key, or unsupported operation.                                        |
|  `401` | A valid TokPortal API key is required.                                                                   |
|  `402` | Insufficient available funds in the separate AI USD wallet. No automatic checkout or debit is initiated. |
|  `403` | The key does not have permission, or explicit AI/billing consent is missing.                             |
|  `404` | The resource does not exist in this workspace.                                                           |
|  `409` | Conflict, expired quote, changed request, or job not cancellable.                                        |
|  `422` | A model capability or input combination is unsupported.                                                  |
|  `429` | Rate, concurrency, or spending limit reached. Retry only as instructed.                                  |
|  `503` | AI is disabled or a required capability, schema, or provider is unavailable. No fallback spend occurs.   |

#### 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 at which the current one-minute budget window rolls over.                                 |
| `Retry-After`               | Seconds to wait before retrying after a `rate_limited` response. Present on 429 responses only.          |
| `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/ai/configure" \
  -H "X-API-Key: sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "resource": "access",
  "config": {
    "enabled": false
  }
}'
```

**Node**

```ts
const response = await fetch('https://app.tokportal.com/api/ext/ai/configure', {
  method: 'PATCH',
  headers: {
    'X-API-Key': process.env.TOKPORTAL_API_KEY!,
    'Content-Type': 'application/json',

  },
  body: JSON.stringify({
  "resource": "access",
  "config": {
    "enabled": false
  }
})
});
const data = await response.json();
```

**Python**

```python


response = requests.request(
    'PATCH',
    'https://app.tokportal.com/api/ext/ai/configure',
    headers={"X-API-Key": os.environ["TOKPORTAL_API_KEY"]},
    json=json.loads("{\n  \"resource\": \"access\",\n  \"config\": {\n    \"enabled\": false\n  }\n}")
)
print(response.json())
```

**Go**

```go
req, _ := http.NewRequest("PATCH", "https://app.tokportal.com/api/ext/ai/configure", strings.NewReader("{\n  \"resource\": \"access\",\n  \"config\": {\n    \"enabled\": false\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)
```
