Source: https://developers.tokportal.com/account-connection-inbox/
Markdown: https://developers.tokportal.com/account-connection-inbox.md

# Account API connection and inbox

Open an account and select **Inbox** in its contextual sidebar. Its tabs are **Comments**, **Direct messages** (Instagram only) and **Seeding**, separate from Account info.
Comments contains comments received on this account's posts. Seeding creates and tracks
paid human comment tasks with this account preselected as the commenter. The account
switcher in the contextual sidebar keeps the current section and chooses the latest
active order's bundle when available.
Desktop uses a list and reading panel; mobile opens each thread with a back
action. Lists load automatically. The same API inbox reads are available through
REST and remote MCP. Inbox GET endpoints are read-only. Public comment replies and Business setup requests use the separate POST operations documented below. Instagram messages can now be answered using the separate write endpoint below.

Base URL: `https://app.tokportal.com/api/ext`. Use your normal workspace API key
with read access. Saved-account IDs belong to the API key's workspace; foreign
and hidden accounts return 404. No account credential reveal is involved.

## Check the connection

`GET /accounts/{id}/connection` · MCP `tokportal_get_account_connection`

The standard `data` envelope contains:

- `account_id`, `version: 1`, `checked_at` and an actionable `message`.
- `account_type`, when available: `business`, `creator`, `personal` or `unknown`.
  This classification does not by itself guarantee messaging eligibility.
- `state`: `connected`, `not_connected`, `action_required` or `checking`.
- `next_action`: `none`, `reconnect`, `instagram_security_check`,
  `check_account_access` or `retry`; `reason` can be null.
- `capabilities`: publishing, analytics and comments booleans, plus
  `dm_configuration` (`eligible`, `business_required`, `not_verified`,
  `unavailable`).

A successful connection check is not proof that every API feature is available.
`dm_configuration=eligible` does not prove an open reply window or successful
message delivery. Recent checks may be reused for up to 60 seconds; a newer
matching disconnection event invalidates them. An unavailable/inconclusive
provider check is `checking`, not a claim that the token was revoked.

For an Instagram checkpoint, the manager first signs in to the exact account in
Instagram, completes the security instructions, then reconnects in TokPortal.
Reconnecting before the check is cleared cannot fix the account. An account
access issue does not by itself prove a permanent ban. Provider-specific
credentials and OAuth URLs are never exposed by this endpoint.

## Read the inbox

`GET /accounts/{id}/inbox` · MCP `tokportal_get_account_inbox`

| Query                         | Meaning                                                                                                                                                        |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind=posts`                  | List the account's posts for comment reading (default).                                                                                                        |
| `kind=comments&reference=...` | Read a listed post's comments.                                                                                                                                 |
| `kind=conversations`          | List conversations on an eligible Instagram Creator or Business connection.                                                                                    |
| `kind=messages&reference=...` | Read a listed conversation, newest messages first.                                                                                                             |
| `reply_reference=...`         | With comments, read replies to this returned comment; retain the original post reference.                                                                      |
| `limit`                       | 1–50, default 20.                                                                                                                                              |
| `min_comments`                | For `kind=posts`: `1` hides posts with no comments; `0` includes them (API default). The UI defaults to `1` and has a toggle. Keep the same value when paging. |
| `cursor`                      | The prior page's opaque `pagination.next_cursor`, with unchanged kind and references.                                                                          |

Start by listing posts or conversations. Use the returned item's `reference`,
not a raw platform ID, for the detail request. References and cursors expire after
one hour and are bound to the account, its current connection, resource type and
parent thread. If `inbox_reference_expired_or_invalid` is returned, list the parent
again. Do not parse or construct references/cursors.

```bash
curl 'https://app.tokportal.com/api/ext/accounts/ACCOUNT_UUID/inbox?kind=posts&limit=20' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

The response is `{ "data": { "account_id": "…", "kind": "posts", "items": [],
"pagination": { "has_more": false, "next_cursor": null }, "checked_at": "…",
"source_updated_at": null, "cache_max_seconds": 600, "read_only": true } }`.

Each item contains `id`, `reference` (nullable), plain `text` and resource-specific
fields: participant name/avatar/unread count; post caption, thumbnail, link and
comment/like counts; message direction, deleted flag and HTTPS attachment links;
or comment author/avatar/hidden/reply count. Images use HTTPS URLs and may expire;
reload the list to refresh them. Missing photos fall back to initials or a post icon.

Post items may also include cached `views`, `save_count` and `metrics_checked_at`.
These reuse TokPortal's existing analytics cache without scraping or requesting a
refresh; saves retain the existing plan gate. Missing or disallowed values are
absent/null, never a manufactured zero. Likes and analytics can have different freshness.

A comment with no text does not prove that its native content is empty. The
current provider comment schema does not expose a sticker/GIF/media attachment
field. The UI shows a discreet label and an original-comment link when supplied.
No provider token, raw metadata or credential is returned. Deleted messages omit
body and attachments. Messages/comments are not copied into TokPortal logs.

Inbox access checks current ownership, identity, live connection, feature
eligibility and the current read-only Coverage state. Paused/lapsed, banned,
revealed/detached and otherwise unavailable managed accounts can return 409.
Opening the inbox never renews Coverage or debits credits.

## Availability and freshness

- Comments: eligible Instagram and TikTok connections, on the account's own posts.
- DMs: eligible Instagram professional accounts (Creator or Business), with granted
  messaging permissions and no messaging restriction. Creator accounts do not
  systematically need Business conversion. Connection readiness is separate from
  recipient eligibility and the platform reply window.
- TikTok DMs are not exposed by this release.
- Separate write routes below handle replies, DMs, moderation and comment-to-DM.
  [Comment task endpoints](https://developers.tokportal.com/comments-overview) remain paid human outreach tasks.
- Comments may lag by up to 10 minutes. Refresh explicitly rather than aggressive
  polling. `checked_at` is the read time, not proof of newly refreshed platform data.
- Instagram pre-connection replay covers up to 500 conversations and the newest
  500 messages per conversation. Replay is asynchronous and emits no inbound
  webhooks. Message Requests are unavailable on the current Instagram Login flow.
- The zero-comment filter is applied after provider pagination, so a page can be empty
  while older commented posts still exist. The UI follows a bounded number of empty
  pages automatically and leaves a Load more action for the remainder.
- Check `has_more` even for an empty page. Continue only if `next_cursor` exists.

## Errors

| HTTP | Action                                                                             |
| ---- | ---------------------------------------------------------------------------------- |
| 400  | Fix the query or refresh an expired reference.                                     |
| 401  | Supply a valid session/API key.                                                    |
| 404  | Re-read your owned account list.                                                   |
| 409  | Check `error.code`: connection, messaging setup, comments or Coverage unavailable. |
| 429  | Wait before retrying.                                                              |
| 503  | Temporary DB/provider failure or partial-feed error; retry later.                  |

A provider response with per-account failures is not treated as an empty inbox.
A valid publishing connection does not bypass missing inbox permissions.

## Webhooks and task lifecycle

Use the [public webhook catalog](https://developers.tokportal.com/webhooks) for supported event names. This
release emits scoped metadata events for connection changes, incoming comments/messages
and durable write results. Fetch current scoped resources after receiving events;
never assume delivery order is state order or use undocumented provider event names.

TokPortal internally receives signed, deduplicated provider connection events.
The server verifies the returned account's identity and current health before a
manager connection task can complete; a redirect, HTTP 200 or error report is
not sufficient. Lost browser returns can be reconciled without reconnecting a
wrong account. Checkpoint recovery is done in the existing manager flow, without
a recording or questionnaire. API reads do not create or complete manager tasks.

Manager remuneration is distinct from client API pricing. Existing migration
reconnection rewards, optional client-requested Business setup and earlier pilot
tests retain their own eligibility and one-time payment rules. Business conversion
is never automatically applied to all accounts and can limit licensed music.

## Seeding in the client Inbox

The Seeding tab uses the existing human commenting workflow, pricing, active-manager
and Coverage checks. It works independently of an API connection. An account-specific
form shows the commenter, target URL, exact text and credit total before submission.
Creating a task requires the workspace spending permission; opening or reading the
history does not charge anything. The existing task management view remains available
for review, cancellation and disputes. API read-only status applies to received
comments and DMs, not to an explicitly ordered human task.

## Reply to a received comment, without credits

`POST /accounts/{id}/inbox/replies` · MCP `tokportal_create_account_comment_reply`

Requires API write permission, an eligible verified connection and current Coverage.
The account must own the post. For third-party posts, use paid human **Seeding**
tasks through the existing Comments API instead.

1. List `kind=posts` and keep the post `reference`.
2. Read `kind=comments&reference=POST_REFERENCE`.
3. Choose a comment with `can_reply=true` and retain its `reply_action_reference`.
4. Send the public reply with a new UUID `request_key`. Reuse this exact key and
   body after an uncertain network result. Do not reuse a key for different text.

```json
{
  "post_reference": "OPAQUE_POST_REFERENCE",
  "comment_reference": "OPAQUE_REPLY_ACTION_REFERENCE",
  "text": "Thanks for asking!",
  "request_key": "11111111-1111-4111-8111-111111111111"
}
```

The comment's ordinary `reference` can only read replies. It cannot authorize a
write. Reply capabilities expire after one hour and are bound to the account,
connection, post and comment. Reload the parent lists when they expire.

The result is in `data`: `id`, `saved_account_id`, `state`, `reply_id`,
`error_code`, `retry_after_seconds`, timestamps and `credits_charged: 0`.
A `succeeded` result includes the platform reply ID. Maximum text is 150
characters for TikTok and 2,200 for Instagram.

`GET /accounts/{id}/inbox/replies/{replyId}` · MCP `tokportal_get_account_comment_reply`
returns the durable operation state. `pending` or `unknown` does not prove
failure. Status checks can positively reconcile a matching reply authored by the
account; an empty or cached thread remains inconclusive. Never blindly resend
an unknown operation, including with a different request key. A per-account
lease serializes sends; `429` and `Retry-After` preserve upstream cooldowns.
`rate_limited` and explicit `failed` operations have not been confirmed as sent;
resolve the reported cause before making a new request.

Replies cost **0 credits**. Reading or sending a free reply never renews Coverage
or debits it implicitly. A missing or unpaid Coverage period blocks the action;
review and restore Coverage separately in Account info. Dry-run validates the
connection, signed references and Coverage without sending or reserving an operation.

## Existing and explicitly requested Instagram Business setup

`GET /accounts/{id}/business-setup` · MCP `tokportal_get_account_business_setup`

Returns `data.task` (nullable) and `price_credits: 5`. Task fields are `id`,
`saved_account_id`, `state`, `credits_paid`, `created_at`, `checked_at` and
`completed_at`. Manager compensation is separate and is not exposed here.

`POST /accounts/{id}/business-setup` · MCP `tokportal_request_account_business_setup`

```json
{ "music_limitations_acknowledged": true }
```

Costs **5 credits once per account** and requires spending permission, eligible
Coverage and an assigned account manager. Repeated requests return the existing
task with `credits_charged: 0`. An account already Business is not charged for a
conversion. Business accounts have a restricted music library, including
commercial tracks. Obtain the client's acceptance before requesting the task.

The manager switches the account, reconnects through the identity-protected
flow, and accepts messaging permissions. Existing request progress appears in
**Account info**: `pending`, `needs_business`, `needs_reconnect`,
`needs_permissions`, `account_issue`, `retry`, `completed` or `cancelled`.
Completion requires the expected identity, platform-reported Business status,
fresh authorization and messaging permissions. It does not override Meta's
messaging windows or guarantee delivery to every recipient.

The former `wants_instagram_business` creation option has been retired from
new bundle requests, generated clients and MCP. Do not include it in
`POST /bundles` or `POST /bundles/bulk`. Existing paid setup tasks continue
through the normal verified flow. The separate, intentionally acknowledged
Business setup endpoint remains available when conversion is actually requested.

Eligible **Creator and Business accounts** can both support Instagram messaging.
Switching to Business is not a general prerequisite. Messaging permissions,
platform restrictions and reply windows still apply. The optional Business
purchase offer is currently hidden from the client Inbox, Account info and
new-account creation form. Existing requests continue normally and their progress
remains visible in Account info. A cached form selection cannot silently add this
surcharge to a new UI order. The separate Business setup endpoint requires an
intentional, explicitly acknowledged request.

Direct messages and comment-to-DM are now available through the separate write endpoints below. TikTok Direct messages are currently not offered.

## Connection retention and Coverage

Account actions and analytics reads require current Coverage where applicable.
Old grandfathered accounts without a Coverage subscription remain exempt.
Cancelling renewal preserves access through the paid/included period, provided
there are no overdue unpaid periods. An expired unpaid period blocks account
features immediately, even before the renewal worker updates the stored status.
Use the existing Coverage reactivation quote and button; automatic renewal
recovery still follows its existing rules.

After 30 days of Coverage unavailability, the server removes the exact API
connection. This does not delete the TokPortal account or its local history.
The cleanup rechecks eligibility under a lease, verifies the connection identity
and confirms provider removal. Restoring Coverage before removal cancels
eligibility; after removal the manager must reconnect through the normal flow.
No other connection or provider authorization is removed.

## Public events

Subscribe through the existing webhook endpoint API:

- `account.connection.connected`: the expected account connection was verified.
- `account.connection.disconnected`: a matching provider disconnection was received.
- `account.connection.removed`: Coverage retention cleanup completed.
- `account.business_setup.requested`, `account.business_setup.updated`,
  `account.business_setup.completed`: manager setup progress.
- `inbox.comment.received`: an incoming comment was reported for an eligible account.
- `inbox.message.received`: an incoming Instagram message was reported for an eligible account.
- `inbox.comment.reply.updated`: a public reply operation changed status.

Events contain resource IDs and safe state metadata, not private message bodies,
access tokens or credentials. Fetch current resources using the workspace API.
Provider webhook availability varies by platform/account permission; absence of
an event does not prove an empty Inbox. Existing data may predate registration.
A durable outbox dispatches within the five-minute maintenance cycle and retries
failed deliveries with the same event ID. Deduplicate by `id` and verify the
existing signature contract. Events can arrive out of order: read current state
instead of treating delivery order as authoritative.

### Creator eligibility validation, 16 October 2026

Controlled text replies in existing conversations succeeded on a Creator and a
Business connection and were confirmed in the returned outgoing history. Separate
comment-to-private-DM calls succeeded on both account types with platform
message IDs and were confirmed in outgoing history after a delay. This does not
prove recipient delivery or reading. Private replies and DM sends are now exposed through the guarded endpoints below.

Business conversion remains an explicit optional 5-credit task with the music
warning; it is not a mandatory step for a Creator whose messaging access works.
No Facebook Page or Facebook Login is required for this standard messaging flow.
Comment webhooks use the native platform post ID rather than the nullable Zernio
internal post ID. Refetch the scoped Inbox using current resource references.

## Hide comments by keyword

In **Inbox → Comments**, select **Moderation**. Add words or phrases, optional
exceptions, choose whole-word matching or substring matching, enable the rule and
select **Save rules**. The preview uses the current form without saving or hiding
anything. Rules are off by default and apply only to new comments after saving.
Your own comments are skipped. An exception always overrides matching keywords.
No DM is sent and no credits are charged.

Instagram and TikTok connections with comment permissions can use this feature.
TikTok needs the supported comments integration, not a separate manager app or
necessarily a Business account. The connection and Coverage are checked again
before every hide. A Coverage issue pauses eligibility; it does not purchase or
renew Coverage automatically.

This is **post-publication moderation**, not a platform pre-publication word
filter. Signed incoming comment events enqueue durable jobs, usually processed
immediately; a worker every five minutes recovers pending jobs. A comment may be
visible before it is hidden. Missing upstream events cannot be moderated by this
flow. Existing comments are not scanned when a rule is enabled.
Safe verification reads can wait 330 seconds for a fresh comment to become
available in the provider cache. Processing stops after three attempts. An
ambiguous external hide is never retried automatically.

### Read rules and results

`GET /accounts/{id}/inbox/moderation` · MCP
`tokportal_get_account_comment_moderation`

The `data` envelope contains `settings`, `history` (latest 20 rows) and
`next_cursor`. Pass `cursor=next_cursor` to read older results. Each result includes
`id`, native `post_id` and `comment_id`, matched configured keywords, state,
`error_code`, timestamps and an expiring account-bound `post_reference` when the
connection still matches. Open that thread with the read-only inbox endpoint,
`kind=comments&reference=post_reference`. Comment bodies and provider credentials
are not persisted in moderation history.

### Save rules

`PUT /accounts/{id}/inbox/moderation` · MCP
`tokportal_update_account_comment_moderation`

Requires API write access. Read `settings.version` first (0 before any rules exist)
and include it in the request. A stale version returns HTTP 409
`moderation_version_conflict`; reload before editing. Rule changes cancel waiting
jobs from the previous version. Disabling remains possible when the connection
or Coverage needs attention.

```json
{
  "enabled": true,
  "keywords": ["spam", "unwanted phrase"],
  "exceptions": ["not spam"],
  "match_mode": "word",
  "version": 0
}
```

Each list accepts up to 100 entries, with 1–80 characters per entry. Matching uses
Unicode NFKC normalization, case normalization and whitespace normalization. In `word`
mode, letters, numbers and underscores are word characters; `contains` matches
literal substrings. Terms are not regular expressions. Enabling requires at least
one keyword. `X-TokPortal-Dry-Run: true` validates without saving or hiding.
Use an `Idempotency-Key` for an exact transport retry; retain the same body/version.

History states:

- `pending` / `processing`: queued or being verified, not proof of hiding.
- `hidden`: provider confirmed the exact comment was hidden.
- `skipped`: rules changed, own/already hidden comment, changed eligibility or text
  no longer matches. Inspect `error_code`.
- `rate_limited`: explicit upstream quota refusal; retry follows its cooldown,
  at most three attempts. Account leases are shared with public comment replies.
- `failed`: a verification or platform restriction needs attention.
- `unknown`: the external write could not be confirmed. Never automatically
  resend it; inspect the original comment and contact support if needed.

Subscribe to `inbox.comment.moderation.updated` for signed result metadata,
then fetch history for the latest state. Webhooks can be retried or delivered out
of order; deduplicate by event ID. No comment text is included.

**Turning moderation off does not restore hidden comments.** TikTok currently
rejects API unhide on our tested connection, even though hide succeeds. Manage
restoration in the platform when available; do not build an automatic undo flow
around an assumed TikTok API success. Instagram hide and unhide have been tested.

## Reply to Instagram direct messages

In **Inbox → Direct messages**, open a conversation and use the message composer.
Eligible Creator and Business accounts work with the standard Instagram connection;
no Facebook Page or forced Business conversion is required. TikTok DMs remain
unavailable. Text replies and comment-to-DM cost **0 credits for this release**.
API sends require a write-enabled key or authorized OAuth grant. Team members
need the workspace write/spending permission even when the credit cost is zero.
Coverage must be usable; writes never buy or renew it.

`POST /accounts/{id}/inbox/messages` · MCP
`tokportal_send_account_inbox_message`

1. Read `GET /accounts/{id}/connection` and check
   `capabilities.dm_configuration == "eligible"`. Account type alone is not proof.
2. Read `GET /accounts/{id}/inbox?kind=conversations` and select its `reference`.
3. Read `kind=messages&reference=…`. The additional `messaging` object reports
   `can_send`, `reply_window_expires_at` and `reason`.
4. Send a plain-text reply using the conversation reference and a new UUID:

```json
{
  "conversation_reference": "<reference from the conversations list>",
  "text": "Thanks for your question. Here are the details you requested.",
  "request_key": "11111111-1111-4111-8111-111111111111"
}
```

The person must have sent a genuine incoming message within the last **24 hours**.
The server checks the most recent 100 messages immediately before reserving an
operation. Without positive evidence it returns HTTP 409
`recipient_must_message_first`; it does not send a cold DM or silently use a
message tag. An outgoing/private reply does not open or extend this window.
Text is limited to 1,000 characters; attachments, message tags, arbitrary recipient
IDs and new cold conversations are not exposed by this release. Reading the Inbox
still sends no read receipt. `read_only: true` describes the GET, not the separate
POST actions.

## Send a manual comment-to-DM

In **Inbox → Comments**, eligible received comments show **Send DM**. This sends
a private plain-text reply rather than a public comment.

`POST /accounts/{id}/inbox/private-replies` · MCP
`tokportal_create_account_private_reply`

Read posts, then comments. Use the **post reference** and the comment's
**private\_reply\_action\_reference**, never its ordinary read-only reference.

```json
{
  "post_reference": "<reference from the posts list>",
  "comment_reference": "<private_reply_action_reference from that post's comments>",
  "text": "Here is the information you asked for in your comment.",
  "request_key": "22222222-2222-4222-8222-222222222222"
}
```

Instagram permits **one private reply per comment, within seven days**. The server
rechecks that the exact comment is on this owned post, is visible, is not by this
account and remains within that period. Manual and automatic sends share the
same consumption protection: another UUID or changed text cannot duplicate a
consumed/uncertain private reply. A platform refusal may consume the slot too.
Creator and Business are supported with comment permissions. Instagram only.
The recipient must message back before follow-up DMs can use the 24-hour window.

## Delivery, retries and idempotency

Both POSTs return the standard `data` envelope containing:

- `id`, `saved_account_id`, `kind` (`dm` or `private_reply`);
- `state`, `message_id`, `error_code`, `retry_after_seconds`;
- `created_at`, `finished_at` and `credits_charged: 0`.

`succeeded` means the provider returned a native message ID, not proof of receipt
or reading. `pending`/`unknown` are unconfirmed outcomes. Never blindly resend
them, even under a new UUID. `failed` is a platform refusal; a private reply can
still be consumed. `rate_limited` means an explicit quota refusal: honor
`Retry-After` and inspect the result before any later attempt.

`GET /accounts/{id}/inbox/operations/{operationId}` · MCP
`tokportal_get_account_message_operation`

This owner-scoped endpoint returns the latest operation. A pending operation older
than two minutes becomes `unknown`, without sending again. Keep `request_key`
identical for an exact transport retry; changed input with that key conflicts.
Supply an `Idempotency-Key` as well when using the API write transport.
A new deliberate send uses a new request UUID. Shared per-account leases serialize
DMs, private replies, public replies and moderation; limits can produce HTTP 429.
`X-TokPortal-Dry-Run: true` validates eligibility without reserving, sending or
charging anything. References expire after one hour; refetch the parent list.

## Automate Instagram comment-to-DM

In **Inbox → Comments → Automations**, configure a keyword rule. There is one
rule per account, applying to all new original comments or one selected post.
Rules are **disabled by default**. Preview/edit the exact plain-text message,
then explicitly save and enable. Moderation remains a separate control.

`GET /accounts/{id}/inbox/comment-to-dm` · MCP
`tokportal_get_account_comment_dm`

The `data` envelope contains `settings`, `history` (20 most recent results) and
`next_cursor`; pass `cursor` for older results. Settings include a fresh signed
`post_reference` for a saved post scope, or null for an account-wide rule. The
message template is private owner configuration. Job history contains IDs and
outcomes, not incoming comment or DM content.

`PUT /accounts/{id}/inbox/comment-to-dm` · MCP
`tokportal_update_account_comment_dm`

```json
{
  "enabled": true,
  "keywords": ["guide", "details"],
  "exceptions": ["no guide"],
  "match_mode": "word",
  "message": "Thanks for asking! Here is the guide: https://example.com/guide",
  "post_reference": null,
  "version": 0
}
```

Read the current `settings.version` first. Pass that version; HTTP 409
`automation_version_conflict` requires a refresh. Enable requires at least one
keyword and a nonempty message. Limits and Unicode matching are identical to
keyword moderation: up to 100 keywords/exceptions, 1–80 characters each,
whole-word or contains, with exceptions taking priority. Templates accept up to
1,000 characters. `post_reference` must come from this account's own posts.
Disable remains possible while Coverage/connection needs attention.

Only genuine **new original comments created after saving** qualify. Old comments,
own comments and replies to comments do not trigger it. There is no historical
scan. Signed provider webhooks enqueue durable, resource-deduplicated jobs; they
normally run immediately, with a five-minute recovery worker. Current ownership,
connection identity, permissions, Coverage, comment text/age and settings version
are rechecked before the external send. Settings changes skip old queued jobs.
If moderation already hid a comment, it is not eligible for a private reply.

Safe read verification and explicit quota refusals can retry at most three times.
Uncertain writes are never automatically resent. History states are `pending`,
`processing`, `succeeded`, `failed`, `unknown`, `skipped`, `rate_limited`. A
connection or Coverage issue stops eligibility; fix it before expecting new sends.
Disable the rule at any time to stop new jobs. This is one private reply, not an
unlimited drip campaign. Missing upstream webhooks cannot trigger an automation.
Dry-run validates settings/version without saving or sending.

## Messaging webhooks

Subscribe through the standard signed TokPortal webhook endpoint:

- `inbox.message.received`: a new eligible Instagram incoming message.
- `inbox.comment.received`: a new comment notification.
- `inbox.message.send.updated`: a DM operation result.
- `inbox.comment.private_reply.updated`: a manual or automatic private-reply result.
- `inbox.comment.automation.updated`: a comment-to-DM job result.
- `inbox.comment.moderation.updated`: a keyword-hide result.
- `account.connection.disconnected`: connection recovery is needed.

Events carry scoped resource IDs and states, never private message bodies or
provider credentials. Deduplicate event IDs; delivery order is not state order.
Refetch operation/history/current connection. A disconnection with
`reason: instagram_checkpoint` means the manager must first open the correct
account in Instagram and complete the security instructions, then reconnect.
The normal manager task system picks up these connections; no client API call
can clear the checkpoint or mark the task done. A transient/inconclusive health
probe is not itself proof of a disconnection.

Complete OpenAPI: [JSON](https://developers.tokportal.com/openapi.json) · [YAML](https://developers.tokportal.com/openapi.yaml).
AI-friendly full documentation: [llms-full.txt](https://developers.tokportal.com/llms-full.txt).

## Delete, hide and pin comments

In **Inbox > Comments**, open the comment's three-dot menu. Only actions actually
allowed by the platform are shown. Deleting requires confirmation and is permanent;
replies may also disappear. No bulk deletion is offered. These actions cost 0 credits.

| Action         | Availability                                                                                                                        |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `delete`       | Instagram/TikTok when the returned comment explicitly permits it. TikTok generally permits deleting your own comments/replies only. |
| `hide`         | Instagram/TikTok when permitted and currently visible.                                                                              |
| `unhide`       | Instagram when permitted and currently hidden. TikTok restoration is withheld in this release.                                      |
| `pin`, `unpin` | TikTok top-level comments, only when the permission and current pinned state are known.                                             |

Likes are not exposed: Instagram's like API needs a different Facebook Login and
restricted Meta approval, and TikTok's endpoint and platform guides disagree.
The absence of an action is not permission to construct your own reference.

1. List owned posts with `GET /accounts/{id}/inbox?kind=posts`.
2. Read comments using the post's signed `reference`. Each comment has `actions`,
   an array of `{ "action": "delete", "reference": "opaque-capability" }` when allowed.
3. Call `POST /accounts/{id}/inbox/comment-actions`:

```json
{
  "post_reference": "SIGNED_POST_REFERENCE",
  "action_reference": "SIGNED_ACTION_REFERENCE",
  "action": "delete",
  "request_key": "11111111-1111-4111-8111-111111111111"
}
```

MCP: `tokportal_create_account_comment_action`. Obtain explicit user confirmation
before permanent deletion. Use `dry_run: true` to verify without modifying comments.
The server rechecks current permissions, ownership, Coverage and connection, including
a reply's signed parent. Expired capabilities require refreshing the thread.

The `data` response contains `id`, `saved_account_id`, `action`, `state`,
`error_code`, `retry_after_seconds`, timestamps and `credits_charged: 0`.
Reuse the same request key for the same intent. An exact completed deletion can be
replayed without requiring the deleted comment to still exist.

Read `GET /accounts/{id}/inbox/comment-actions/{actionId}` or MCP
`tokportal_get_account_comment_action` for durable status. A confirmed provider
acknowledgement is needed for `succeeded`. `pending`/`unknown` is not a failure or
permission to resend; the same ambiguous action is protected even with a new key.
A comment missing from a cached page alone does not prove successful deletion.
Subscribe to `inbox.comment.action.updated` for scoped result metadata (account,
operation, action, post/comment IDs, state, error and cooldown), without comment text.

## Usage limits and account safety

TokPortal serializes manual actions with replies, DMs and automatic moderation using
a shared account lease. Current platform permissions and reply windows are checked
before writes. Keyword rules process only new matching incoming comments, skip own
comments and avoid reply loops. They do not perform historical mass actions.

Honor HTTP 429 and `Retry-After`, including responses without a machine-readable
error code. TokPortal recognizes provider header seconds/dates, documented body
cooldowns and rate-limit reset headers. A provider cooldown also pauses other Inbox
writes for that account. Wait before retrying; do not create API keys to evade a
shared quota. There is no automatic resend after an ambiguous write or retry loop
after a platform refusal.

Use incoming webhooks to fetch changed threads and bounded pagination to browse
history. Avoid aggressive polling or bursts. Posting caps are separate from DM and
comment limits. Technical quotas are ceilings, not recommended activity targets or
a guarantee against spam detection. Account age, normal use, content and platform
security checks can still lead to restrictions. A checkpoint requires the manager
to clear Instagram's check before reconnecting; it does not prove a permanent ban.
Never automatically change customer captions or resume spam-blocked publishing.

Provider references: [Zernio rate limits](https://docs.zernio.com/guides/rate-limits),
[error handling](https://docs.zernio.com/guides/error-handling),
[comment deletion](https://docs.zernio.com/comments/delete-inbox-comment).
