> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to the Zernio MCP server

The Zernio MCP connector routes your AI agent's tool calls to Zernio's own MCP server through Scalekit. Each user signs in to Zernio once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 54 tools.

**Authentication:** OAuth 2.1/DCR
**Categories:** Marketing, Communication
**Tools:** 54: 29 read, 0 write, 25 destructive
**Users sign in with:** OAuth
**OAuth app:** Your own Zernio MCP server app
**Built by:** Vendor MCP
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=ZERNIOMCP)

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the Zernio MCP connection

   In **AgentKit > Connections**, create a Zernio MCP connection and copy its redirect URI. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

4. ### Register an OAuth app

   Zernio MCP server connections use your own OAuth app. Register one with Zernio MCP server and add the redirect URI you copied.

   Then enter the app's Client ID and Client Secret on the Zernio MCP connection.

5. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'zerniomcp'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize Zernio MCP:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'zerniomcp_account_groups_list_account_groups',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "zerniomcp"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize Zernio MCP:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="zerniomcp_account_groups_list_account_groups",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user signs in once. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'zerniomcp_account_groups_list_account_groups',
  toolInput: {},
  connector: 'zerniomcp',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="zerniomcp_account_groups_list_account_groups",
    tool_input={},
    connection_name="zerniomcp",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `zerniomcp_account_groups_list_account_groups`

List groups · Read-only

List groups

Returns all account groups visible to the authenticated user. Groups can
contain accounts from multiple profiles. For API keys scoped to specific
profiles, only groups whose accounts all live in allowed profiles are
returned.

Read only: yes. Destructive: no.

Inputs: none.

Example input: `{}`

### `zerniomcp_accounts_get`

Get account details for a specific platform · Read-only

Get account details for a specific platform.

Returns username and ID for the first account matching the platform.

Read only: yes. Destructive: no.

Inputs:

- `platform` (`string`, required): Platform name: twitter, instagram, linkedin, tiktok, bluesky, facebook, youtube, pinterest, threads (required)

Example input: `{"platform":"twitter"}`

### `zerniomcp_accounts_get_account_health`

Check account health · Read-only

Check account health

Returns detailed health info for a specific account including token status, permissions, and recommendations.

A failing or stalled analytics sync (see `analyticsSync`) raises a healthy account to at least `warning`.

### Facebook, Instagram and Threads

The token is also probed live. A probe Meta refuses with a dead-token error sets `tokenStatus.valid` to `false` and names Meta's code, subcode and fbtrace in `issues`.

The exception: if an analytics sync succeeded with that token in the last six hours, the token stays `valid` and `issues` reports the probe as inconclusive, since Meta answers a transient 190 to live tokens now and then.

Neither verdict disconnects the account. That happens only when a real operation fails, and `account.disconnected` is sent.

### WhatsApp

The response also includes `platformConnection`, a live probe of the Meta link behind the channel (the same read as `GET /v1/whatsapp/number-info`).

The OAuth token can be perfectly valid while Meta refuses to ...

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, required): The account ID to check (required)

Example input: `{"account_id":"<account_id>"}`

### `zerniomcp_accounts_get_all_accounts_health`

Check accounts health · Read-only

Check accounts health

Returns health status of all connected accounts including token validity, permissions, and issues needing attention. A failing or stalled analytics sync (see `analyticsSync`) raises a healthy account to at least warning.

Read only: yes. Destructive: no.

Inputs:

- `platform` (`string`, optional): Filter by platform. One of: `facebook`, `instagram`, `linkedin`, `twitter`, `tiktok`, `youtube`, `threads`, `pinterest`, `reddit`, `bluesky`, `googlebusiness`, `telegram`, `snapchat`, `discord`, `slack`, `whatsapp`, `shopify`, `wordpress`, `wix`, `linkedinads`, `metaads`, `pinterestads`, `tiktokads`, `xads`, `googleads`, `openaiads`, `redditads`, `microsoftads`, `whopads`, `hubspot`, `webflow`, `googleanalytics`.
- `profile_id` (`string`, optional): Filter by profile ID
- `status` (`string`, optional): Filter by health status. One of: `healthy`, `warning`, `error`.

Example input: `{}`

### `zerniomcp_accounts_get_follower_stats`

Get follower stats · Read-only

Get follower stats

Returns follower count history and growth metrics for connected accounts.
Requires analytics add-on subscription. Follower counts are refreshed once per day.

Read only: yes. Destructive: no.

Inputs:

- `account_ids` (`string`, optional): Comma-separated list of account IDs (optional, defaults to all user's accounts)
- `cursor` (`string`, optional): Opaque cursor from a previous response's `pagination.nextCursor`; returns the next page of `limit` accounts (25 when limit is omitted). A cursor we cannot parse returns 400.
- `from_date` (`string`, optional): Start date in YYYY-MM-DD format (defaults to 30 days ago)
- `granularity` (`string`, optional): Data aggregation level. One of: `daily`, `weekly`, `monthly`. Default: `daily`.
- `limit` (`integer`, optional): Accounts per page, ordered by account id. Send it to page through accounts; the response then carries `pagination.nextCursor`. Without limit or cursor every matching account is returned.
- `profile_id` (`string`, optional): Filter by profile ID
- `to_date` (`string`, optional): End date in YYYY-MM-DD format (defaults to today)

Example input: `{}`

### `zerniomcp_accounts_list`

List connected social media accounts, one page at a time · Read-only

List connected social media accounts, one page at a time.

Returns the platform, username, and account ID for each account on the page.
Use this to find account IDs needed for creating posts. In a large workspace
filter by platform, profile_id or search instead of paging through everything.

Read only: yes. Destructive: no.

Inputs:

- `cursor` (`string`, optional): Cursor returned by the previous page. Keep the same filters Default: . Default: ``.
- `limit` (`integer`, optional): Page size, up to 500 Default: 100. Default: `100`.
- `platform` (`string`, optional): Only accounts on this platform (e.g., 'twitter') Default: . Default: ``.
- `profile_id` (`string`, optional): Only accounts in this profile Default: . Default: ``.
- `search` (`string`, optional): Text matched against account ID, username, display name and platform user ID Default: . Default: ``.

Example input: `{}`

### `zerniomcp_analytics_get_analytics`

Get post analytics · Read-only

Get post analytics

Returns analytics for posts. With postId, returns a single post. Without it, returns a paginated list with overview stats.

- Accepts both Zernio Post IDs and External Post IDs (auto-resolved).
- fromDate defaults to 90 days ago if omitted, max range 366 days.
- Single post lookups may return 202 (sync pending) or 424 (all platforms failed).
- For follower stats, use /v1/accounts/follower-stats.

### LinkedIn personal accounts

Analytics are only available for posts published through Zernio. LinkedIn's API only returns metrics for posts authored by the authenticated user. Organization/company page analytics work for all posts.

### Facebook Page stories

Stories on a connected Page, published through Zernio or natively, are collected while they are live and returned like any other post (platformPostUrl points to the story). Metrics for a story published through Zernio are available on its Zernio post ID.

Their lifetime metrics refresh about hourly, with a final reading shortly ...

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Filter by account ID. With a native postId, selects that account's copy of the post.
- `from_date` (`string`, optional): Inclusive lower bound (YYYY-MM-DD). Defaults to 90 days ago if omitted. Max range is 366 days.
- `limit` (`integer`, optional): Page size (default 50). Default: `50`.
- `order` (`string`, optional): Sort order. One of: `asc`, `desc`. Default: `desc`.
- `page` (`integer`, optional): Page number (default 1). Default: `1`.
- `platform` (`string`, optional): Filter by platform (default "all")
- `post_id` (`string`, optional): Returns analytics for a single post. Accepts Zernio Post IDs, External Post IDs and the platform's native post ID. Zernio IDs are auto-resolved to External Post analytics.
- `profile_id` (`string`, optional): Filter by profile ID (default "all"). With a native postId, selects that profile's copy of the post.
- `sort_by` (`string`, optional): Sort by date, engagement, or a specific metric. Platform-specific metrics (follows, reposts, reels_skip_rate, ig_reels_*, completion_rate, profile_views, website_clicks) sort a null value as 0. One of: `date`, `engagement`, `impressions`, `reach`, `likes`, `comments`, `shares`, `saves`, `clicks`, `views`, `follows`, `ig_reels_avg_watch_time`, `ig_reels_video_view_total_time`, `reposts`, `reels_skip_rate`, `completion_rate`, `profile_views`, `website_clicks`. Default: `date`.
- `source` (`string`, optional): Filter by post source: late (posted via Zernio API), external (synced from platform), all (default). One of: `all`, `late`, `external`. Default: `all`.
- `tags` (`string`, optional): Comma-separated tags (at most 20). Lists only posts created through Zernio that carry every listed tag, matched exactly and case-sensitively. Up to 200 published posts can match within the date range; more returns 400 asking to narrow fromDate and toDate. Ignored when postId is set.
- `to_date` (`string`, optional): Inclusive upper bound (YYYY-MM-DD). Defaults to today if omitted.

Example input: `{}`

### `zerniomcp_analytics_get_best_time_to_post`

Get best times to post · Read-only

Get best times to post

Returns the best times to post based on historical engagement data.
Groups all published posts by day of week and hour (UTC), calculating average engagement per slot.
Use this to auto-schedule posts at optimal times.

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Filter by account ID. Omit for all accounts.
- `platform` (`string`, optional): Filter by platform (e.g. "instagram", "tiktok"). Omit for all platforms.
- `profile_id` (`string`, optional): Filter by profile ID. Omit for all profiles.
- `source` (`string`, optional): Filter by post origin. "late" for posts published via Zernio, "external" for posts imported from platforms. One of: `all`, `late`, `external`. Default: `all`.

Example input: `{}`

### `zerniomcp_analytics_get_daily_metrics`

Get daily aggregated metrics · Read-only

Get daily aggregated metrics

    Returns daily aggregated analytics metrics and a per-platform breakdown.
    Each day includes post count, platform distribution, and summed metrics (impressions, reach, likes, comments, shares, saves, clicks, views).
    Defaults to the last 180 days.

    `totals` sums the whole range for the applied filters (pass `accountId` for one account). With attribution "publish" they are the lifetime metrics of posts published in the range, with "received" the engagement that arrived in the range.

    Args:
        platform: Filter by platform (e.g. "instagram", "tiktok"). Omit for all platforms.
        profile_id: Filter by profile ID. Omit for all profiles.
        account_id: Filter by account ID
        from_date: Inclusive start date (ISO 8601). Defaults to 180 days ago.
        to_date: Inclusive end date (ISO 8601). Defaults to now.
        source: Filter by post origin. "late" for posts published via Zernio, "external" for posts imported from platforms.
        attribution: How each post's engagement is attributed to a day.

- "publish" (default): sums each post's lifetime total on its publish date.
- "received": buckets the per-day increase in engagement by the day it actually arrived (engagement-over-time), so engagement on older posts appears on the day it was gained rather than the post's publish date.

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Filter by account ID. Omit for all accounts.
- `attribution` (`string`, optional): How each post's engagement is attributed to a day. publish sums each post's lifetime total on its publish date. received buckets the per-day increase by the day it arrived. One of: `publish`, `received`. Default: `publish`.
- `from_date` (`string`, optional): Inclusive start date. Omit to use the default range described in the tool description.
- `platform` (`string`, optional): Filter by platform, for example instagram or tiktok. Omit for all platforms.
- `profile_id` (`string`, optional): Filter by profile ID. Omit for all profiles.
- `source` (`string`, optional): Filter by post origin. late means posted via Zernio, external means synced from the platform, all returns both. One of: `all`, `late`, `external`. Default: `all`.
- `to_date` (`string`, optional): Inclusive end date. Omit to use the current date.

Example input: `{}`

### `zerniomcp_analytics_get_post_timeline`

Get post analytics timeline · Read-only

Get post analytics timeline

   Returns a daily timeline of analytics metrics for a specific post, showing how impressions, likes,
   and other metrics evolved day-by-day since publishing. Each row represents one day of data per platform.
   For multi-platform Zernio posts, returns separate rows for each platform.

   Args:
       post_id: The post to fetch timeline for. Accepts an ExternalPost ID, a platformPostId, or a Zernio Post ID.
(required)
       from_date: Start of date range (ISO 8601). Defaults to 90 days ago.
       to_date: End of date range (ISO 8601). Defaults to now.

Read only: yes. Destructive: no.

Inputs:

- `post_id` (`string`, required): The post to fetch. Accepts a Zernio post ID, an external post ID or a platform-specific post ID.
- `from_date` (`string`, optional): Inclusive start date. Omit to use the default range described in the tool description.
- `to_date` (`string`, optional): Inclusive end date. Omit to use the current date.

Example input: `{"post_id":"<post_id>"}`

### `zerniomcp_comments_get_inbox_post_comments`

Get post comments · Read-only

Get post comments

    Fetch comments for a specific post. Requires accountId query parameter.

    ### Replies to a comment

    Pass `commentId` (Facebook, Instagram, Reddit, TikTok) to fetch replies to a specific comment instead of the post's top-level comments.

    - Facebook, Instagram and TikTok return the comment's replies, paged by `limit`/`cursor`.
    - Reddit returns the focused comment thread instead.
    - On Facebook and Instagram the requested comment itself comes back in the top-level `comment` field.

    On Facebook, passing a COMMENT id as `postId` (instead of using `commentId`) is also supported for backwards compatibility. It returns that comment's replies the same way.

    Prefer `commentId` for new integrations; it also works on Instagram, which rejects a comment id passed as `postId`. YouTube does not support either form: `postId` must be a video id.

    ### Caching

    Responses are cached for up to 10 minutes, so a page may lag new comments by that window.

    Do not poll this endpoint for real-time updates: ...

    Args:
        post_id: Zernio post ID or platform-specific post ID. Zernio IDs are auto-resolved.

- **LinkedIn:** pass the post id exactly as the feed returns it (urn:li:share:<id> or urn:li:ugcPost:<id>). A bare number is read as an activity id (urn:li:activity:<n>, the number in a linkedin.com/feed/update URL), so a share or ugcPost number passed bare returns an empty list.
- **Facebook:** a comment ID is also accepted and returns that comment's replies (kept for backwards compatibility). Prefer the `commentId` query parameter, which also works on Instagram.
 (required)
        account_id: (required)
        subreddit: (Reddit only) Subreddit name
        limit: Maximum number of comments to return
        cursor: Pagination cursor, returned by a previous call as `pagination.cursor`. This is the platform's own opaque paging value passed through verbatim: never construct, decode or validate it client-side.
        comment_id: (Facebook, Instagram, Reddit and TikTok) Get replies to a specific comment. On Facebook and Instagram, the requested comment is returned in the top-level `comment` field and comments[] holds its replies.

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, required): Filter by account ID. Omit for all accounts.
- `post_id` (`string`, required): The post to fetch. Accepts a Zernio post ID, an external post ID or a platform-specific post ID.
- `comment_id` (`string`, optional): Comment ID. For fetching, returns replies to that comment (Facebook, Instagram, Reddit and TikTok). For replying, replies to that specific comment.
- `cursor` (`string`, optional): Pagination cursor returned by a previous call. Pass it back verbatim.
- `limit` (`integer`, optional): Maximum number of items to return per page. Default: `25`.
- `subreddit` (`string`, optional): (Reddit only) Subreddit name.

Example input: `{"account_id":"<account_id>","post_id":"<post_id>"}`

### `zerniomcp_comments_list_inbox_comments`

List commented posts · Read-only

List commented posts

    Returns posts with comment counts from all connected accounts. Aggregates data across multiple accounts.

    ### Caching

    Responses are cached for up to 10 minutes, so the feed may lag new comments by that window.

    Do not poll this endpoint for real-time updates. Subscribe to the `comment.received` webhook, which fires for every new comment across your posts and carries the post reference needed to keep this list current.

    ### Meta ads

    For users with the Ads add-on (accounts on usage-based billing always qualify), the user's Meta ads (boosted/dark posts) are included too.

    There's one row per (ad, placement-with-comments). An ad that runs on both Facebook feed and Instagram feed produces up to two rows, since the Page dark post and the IG media have separate comment threads.

    Each ad row is flagged `isAd: true` with `adId` and `placement` (`id` is `{adId}:{placement}`). Filter with `platform`:

    - `?platform=metaads`: *only* ad rows.
    - `facebook`/`instagram`: *organic* posts only (no ...

    Args:
        profile_id: Filter by profile ID
        platform: Filter by platform. `metaads` is a synthetic value meaning the user's ads (boosted/dark posts) only; `facebook`/`instagram` return organic posts only. `tiktok` covers accounts connected through the TikTok Business app only; developer-app TikTok accounts are returned under `meta.accountsSkipped`.
        min_comments: Minimum comment count, applied to each page after pagination
        since: Posts created after this date
        sort_by: Sort field
        sort_order: Sort order
        limit
        cursor
        account_id: Filter by specific account ID.

With `platform=metaads` it takes the Meta Ads account ID, or the Facebook or Instagram account ID the Meta Ads connection belongs to, which returns that connection's ad rows.

A Facebook or Instagram account with no Meta Ads connection returns no rows. An account on any other platform returns 400.

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Filter by account ID. Omit for all accounts.
- `cursor` (`string`, optional): Pagination cursor returned by a previous call. Pass it back verbatim.
- `limit` (`integer`, optional): Maximum number of items to return per page. Default: `50`.
- `min_comments` (`integer`, optional): Minimum comment count, applied to each page after pagination.
- `platform` (`string`, optional): Filter by platform. metaads returns only the user's ads; facebook and instagram return organic posts only. One of: `facebook`, `instagram`, `twitter`, `bluesky`, `threads`, `youtube`, `linkedin`, `reddit`, `tiktok`, `metaads`.
- `profile_id` (`string`, optional): Filter by profile ID. Omit for all profiles.
- `since` (`string`, optional): Only posts created after this date (ISO 8601).
- `sort_by` (`string`, optional): Field to sort by. One of: `date`, `comments`. Default: `date`.
- `sort_order` (`string`, optional): Sort order. One of: `asc`, `desc`. Default: `desc`.

Example input: `{}`

### `zerniomcp_docs_search`

Search the Late API documentation · Read-only

Search across the Late API documentation to find relevant information, code examples, API references, and guides.

Use this tool when you need to answer questions about Late, find specific documentation, understand how features work, or locate implementation details.

The search returns contextual content with section titles and relevant snippets.

Read only: yes. Destructive: no.

Inputs:

- `query` (`string`, required): Search query (e.g., 'webhooks', 'create post', 'authentication') (required)

Example input: `{"query":"webhooks"}`

### `zerniomcp_media_check_upload_status`

Check upload status and get file URLs · Read-only

Check the status of an upload token and get uploaded file URLs.

Use this after the user has uploaded files through the browser upload page.
Returns: pending (waiting for upload), completed (files ready), or expired (token expired).

Read only: yes. Destructive: no.

Inputs:

- `token` (`string`, required): The upload token from media_generate_upload_link (required)

Example input: `{"token":"<token>"}`

### `zerniomcp_mentions_list_inbox_mentions`

List mentions · Read-only

List mentions

Returns mentions of your connected accounts from the last 7 days. Requires Inbox addon.

### LinkedIn

Organization mentions, delivered by webhooks and a background pull.

### Instagram

Comment mentions (someone tags the account in a comment on their own media) and caption mentions (someone tags it in their media caption), delivered by the Meta `mentions` webhook.

- Only accounts connected through Facebook Login receive them.
- Comments on the account's own posts are never listed, even when they contain its @handle.
- Their text is read from Instagram in the background after the first request, so `content` can be empty on the first read.
- Pass `mediaId` and `commentId` to `POST /v1/inbox/mentions/reply` to answer one.

### Facebook

Post mentions (someone tags the Page in a post) and comment mentions (someone tags it in a comment), delivered by the Page `mention` webhook with their text.

- Comments on the Page's own posts and anything the Page wrote itself are never listed.
- ...

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Filter by account ID
- `cursor` (`string`, optional): Cursor for pagination (ID of the last item from the previous page)
- `limit` (`integer`, optional): Maximum number of items to return per page. Default: `25`.
- `profile_id` (`string`, optional): Filter by profile ID
- `sort_order` (`string`, optional): Sort order by publishedAt. One of: `asc`, `desc`. Default: `desc`.

Example input: `{}`

### `zerniomcp_posts_get`

Get details of a specific post · Read-only

Get full details of a specific post including content, status, and scheduling info.

Read only: yes. Destructive: no.

Inputs:

- `post_id` (`string`, required): The post ID to retrieve (required)

Example input: `{"post_id":"<post_id>"}`

### `zerniomcp_posts_list`

List posts with optional filtering · Read-only

List posts with optional filtering by status.

Status options: draft, scheduled, published, failed

Read only: yes. Destructive: no.

Inputs:

- `limit` (`integer`, optional): Maximum number of posts to return Default: 10. Default: `10`.
- `status` (`string`, optional): Filter by status: draft, scheduled, published, failed. Leave empty for all posts Default: . Default: ``.

Example input: `{}`

### `zerniomcp_posts_list_failed`

List all failed posts · Read-only

List all failed posts that can be retried.

Read only: yes. Destructive: no.

Inputs:

- `limit` (`integer`, optional): Maximum number of posts to return Default: 10. Default: `10`.

Example input: `{}`

### `zerniomcp_profiles_get`

Get details of a specific profile · Read-only

Get details of a specific profile including name, description, and color.

Read only: yes. Destructive: no.

Inputs:

- `profile_id` (`string`, required): The profile ID (required)

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_profiles_list`

List profiles, one page at a time · Read-only

List profiles, one page at a time.

Profiles group multiple social accounts together for easier management.

Read only: yes. Destructive: no.

Inputs:

- `limit` (`integer`, optional): Page size, up to 1000 Default: 100. Default: `100`.
- `name` (`string`, optional): Only the profile with exactly this name Default: . Default: ``.
- `skip` (`integer`, optional): How many profiles to skip, for the next page Default: 0. Default: `0`.

Example input: `{}`

### `zerniomcp_queue_get_next_queue_slot`

Get next available slot · Read-only

Get next available slot

Returns the next available queue slot for preview purposes. To create a queue post, use POST /v1/posts with queuedFromProfile instead of scheduledFor.

Read only: yes. Destructive: no.

Inputs:

- `profile_id` (`string`, required): (required)
- `queue_id` (`string`, optional): Specific queue ID (optional, defaults to profile's default queue)

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_queue_list_queue_slots`

List schedules · Read-only

List schedules

Returns queue schedules for a profile. Use all=true for all queues, or queueId for a specific one. Defaults to the default queue.

Read only: yes. Destructive: no.

Inputs:

- `profile_id` (`string`, required): Profile ID to get queues for (required)
- `all` (`string`, optional): Set to 'true' to list all queues for the profile
- `queue_id` (`string`, optional): Specific queue ID to retrieve (optional)

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_queue_preview_queue`

Preview upcoming slots · Read-only

Preview upcoming slots

Returns the next N upcoming queue slot times for a profile as ISO datetime strings.

Read only: yes. Destructive: no.

Inputs:

- `profile_id` (`string`, required): (required)
- `count` (`integer`, optional): No description. Default: `20`.
- `queue_id` (`string`, optional): Filter by specific queue ID. Omit to use the default queue.

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_search_tools`

Search available tools · Read-only

Search for tools using natural language.

Returns matching tool definitions ranked by relevance,
in the same format as list_tools.

Read only: yes. Destructive: no.

Inputs:

- `query` (`string`, required): Natural language query to search for tools

Example input: `{"query":"webhooks"}`

### `zerniomcp_support_assistant_get_support_answer`

Get a support answer · Read-only

Get a support answer

Returns a run started by your team, with the answer once it is ready.

Prefer the `support.run.completed` and `support.run.failed` webhooks. Use this as the fallback, waiting `pollAfterSeconds` between polls.

`costUsd` is the amount billed for the question, never above `maxCostUsd`, and 0 for a failed run (pricing on Ask a support question).

Read only: yes. Destructive: no.

Inputs:

- `run_id` (`string`, required): (required)

Example input: `{"run_id":"<run_id>"}`

### `zerniomcp_tracking_tags_get_tracking_tag_stats`

Get aggregated event stats · Read-only

Get aggregated event stats

Shows event counts or health for a tracking tag so you can confirm it is firing, where the platform reports them.

Platforms without a stats API answer 501.

### Meta

Aggregated counts (`GET /{pixel_id}/stats`), rows passed through as-is. Their shape depends on the `aggregation` requested.

### OpenAI Ads

The recent-events stream (`GET /conversions/events`): the latest (at most 50) events received in the last 15 minutes, one row per event (`event_type`, `api_channel`, `event_timestamp_ms`, `received_at_ms`, ...).

Both sources appear in `api_channel`:

- `pixel_sdk`: the on-site Pixel (including its `openai::sdk_init` load event)
- `server_to_server`: Conversions API events

It is a fixed window: `startTime`/`endTime` answer 400. Use it to confirm an install fires; attributed totals come from ads analytics.

Accounts not enabled for the stream answer 422 `feature_not_available`.

### LinkedIn (`linkedinads`)

Health rows rather than counts, since LinkedIn exposes no per-event fire ...

Platforms: meta, tiktok, google, x, openai, linkedin, pinterest

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, required): (required)
- `tag_id` (`string`, required): Tag id (`TrackingTag.id`). (required)
- `ad_account_id` (`string`, optional): Scopes the lookup on platforms whose tag ids live inside an ad account. Ignored elsewhere.
- `aggregation` (`string`, optional): Meta only (400 on other platforms): aggregation dimension. Defaults to `event`. One of: `event`, `host`, `url`, `url_by_rule`, `pixel_fire`, `device_type`, `device_os`, `browser_type`, `had_pii`, `custom_data_field`, `match_keys`, `event_source`, `event_detection_method`, `event_processing_results`, `event_total_counts`, `event_value_count`. Default: `event`.
- `end_time` (`string`, optional): Unix seconds upper bound.
- `start_time` (`string`, optional): Unix seconds lower bound.

Example input: `{"account_id":"<account_id>","tag_id":"<tag_id>"}`

### `zerniomcp_tracking_tags_list_tracking_tags`

List tracking tags · Read-only

List tracking tags

Lists the pixels and other measurement tags an ads account can see, so you can inspect or install them.

The list view omits `code`. Call `getTrackingTag` for the install snippet and full detail.

### Meta and OpenAI Ads

Returns the tracking tags (Meta Pixels, or OpenAI Ads pixels) the connected ads account can see. Meta is platform `metaads` and OpenAI Ads is platform `openaiads`. Platforms without a section below return 501.

- Pass `?adAccountId=act_...` (Meta only) to scope the list to a single ad account.
- Omit it to list every pixel reachable by the token. The name is then suffixed with the ad account it was discovered on, for disambiguation.
- `adAccountId` is ignored for OpenAI Ads (one API key maps to exactly one ad account).

The `accountId` must be the ads SocialAccount created by the Ads add-on connect flow (Meta) or the OpenAI Ads connect flow, not a Facebook/Instagram posting account. Get your Meta `act_...` ids from `GET /v1/ads/accounts`.

### LinkedIn ...

Platforms: meta, tiktok, google, x, openai, linkedin, pinterest

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, required): Ads SocialAccount id (platform `metaads` or `openaiads`). (required)
- `ad_account_id` (`string`, optional): Optional, Meta only. Scope to one ad account, e.g. `act_123456789`. Ignored for OpenAI Ads.

Example input: `{"account_id":"<account_id>"}`

### `zerniomcp_usage_get_usage`

Usage snapshot (default) or billed-spend metering (with params) · Read-only

Usage snapshot (default) or billed-spend metering (with params)

    Dual-mode endpoint, selected by query params, and fully backward compatible.

    ### Without metering params (the default)

    Returns the plan / quota / usage snapshot: plan name, billing period, limits, usage counts, access state. Identical to `GET /v1/usage-stats`. Existing integrations keep working unchanged.

    ### With `range`, `granularity`, `from`, or `to`

    Returns usage METERING: billed spend (USD) by product family over the window, at `day` / `month` / `total` granularity. Product families: `accounts`, `numbers`, `imessage`, `calls`, `sms`, `messages`, `verify`, `dlc`, `xApi`, `ads`, `credits`, `other`.

    Data comes from the usage-based invoice breakdown (the CHARGE view, which always reconciles with what gets billed). Also served at `GET /v1/usage/daily`.

    Usage-based accounts only: legacy Stripe accounts get `{ "supported": false, "days": [] }`.

    ### Attribution (metering mode)

    `groupBy=profile|account` adds an `attribution` breakdown of the window's spend per profile or account. ...

    Args:
        reconcile: Snapshot mode only. For Stripe subscription users, `true` forces a
subscription reconciliation pass even when cached plan data looks
complete.
        range: Window to report. `cycle` / `prev-cycle` resolve to the customer's
real billing-period bounds (falling back to a trailing 30 days when
no invoice exists yet); `7d`…`12mo` are trailing windows; `custom`
uses `from` / `to`.
        from_: Inclusive start (UTC date). Required when `range=custom`.
        to: Inclusive end (UTC date). Required when `range=custom`. Max span 366 days.
        granularity: Bucketing of the `days` series: `day` (one row per UTC day),
`month` (one row per calendar month, dated to the 1st), or `total`
(no series, read `totals`). Does not affect `totals`.
        group_by: Metering mode. Adds `attribution`: the window's spend split per profile or per account (keys are ids; resolve names via `GET /v1/profiles` / `GET /v1/accounts`).
        profile_id: Metering mode (pair with `range`). Project the payload onto this profile's attributed share. Mutually exclusive with `accountId`, and `groupBy` (if given) must be `profile`; 404 when the profile is not in your team (or outside a scoped key's profiles).
        account_id: Metering mode (pair with `range`). Project the payload onto this account's attributed share. Mutually exclusive with `profileId`, and `groupBy` (if given) must be `account`; 404 when the account is not visible to the caller.

Read only: yes. Destructive: no.

Inputs:

- `account_id` (`string`, optional): Metering mode (pair with range). Project the payload onto this account's attributed share. Mutually exclusive with profile_id; group_by (if given) must be account.
- `from_` (`string`, optional): Inclusive start (UTC date). Required when range is custom.
- `granularity` (`string`, optional): Bucketing of the days series: day (one row per UTC day), month (one row per calendar month, dated to the 1st), or total (no series, read totals). Does not affect totals. One of: `day`, `month`, `total`. Default: `day`.
- `group_by` (`string`, optional): Metering mode. Adds attribution: the window spend split per profile or per account.
- `profile_id` (`string`, optional): Metering mode (pair with range). Project the payload onto this profile's attributed share. Mutually exclusive with account_id; group_by (if given) must be profile.
- `range` (`string`, optional): Window to report. cycle and prev-cycle resolve to the real billing-period bounds (falling back to a trailing 30 days when no invoice exists yet); 7d to 12mo are trailing windows; custom uses from_ and to. One of: `cycle`, `prev-cycle`, `7d`, `14d`, `30d`, `3mo`, `12mo`, `custom`. Default: `cycle`.
- `reconcile` (`string`, optional): Snapshot mode only. For Stripe subscription users, true forces a subscription reconciliation pass even when cached plan data looks complete.
- `to` (`string`, optional): Inclusive end (UTC date). Required when range is custom. Max span 366 days.

Example input: `{}`

### `zerniomcp_zernio_overview`

Zernio server overview · Read-only

Show an overview of what this Zernio MCP server can do (accounts, posts, analytics, ads, inbox) and how to find the right tool.

Read only: yes. Destructive: no.

Inputs: none.

Example input: `{}`

### `zerniomcp_accounts_move_account_to_profile`

Move account to another profile · Destructive

Move account to another profile

Moves a connected account to a different profile owned by the same
user. The target profile must belong to the same user as the account.

For API keys restricted to specific profiles, BOTH the source account's
current profile AND the target profile must be in the key's allowed set.
Calls with a target profile outside the key's scope return 403.

Read only: no. Destructive: yes.

Inputs:

- `account_id` (`string`, required): (required)
- `profile_id` (`string`, required): Target profile ID (must be a valid ObjectId and owned by the same user as the account). (required)

Example input: `{"account_id":"<account_id>","profile_id":"<profile_id>"}`

### `zerniomcp_call_tool`

Call a tool discovered via search · Destructive

Call a tool by name with the given arguments.

Use this to execute tools discovered via search_tools.

Read only: no. Destructive: yes.

Inputs:

- `name` (`string`, required): The name of the tool to call
- `arguments` (`string`, optional): Arguments to pass to the tool

Example input: `{"name":"<name>"}`

### `zerniomcp_comments_reply_to_inbox_post`

Reply to comment · Destructive

Reply to comment

Post a reply to a post or specific comment. Requires accountId in request body.

### Idempotency

Send an `Idempotency-Key` header to make retries safe (e.g. after a client-side timeout where delivery is unknown).

- Same key + same body replays the original response (with `Idempotent-Replayed: true`) instead of posting the comment a second time.
- Same key + different body returns 422.
- A key still in flight returns 409.

Keys are retained for 24 hours and are scoped to the credential and to this exact path. Reusing a key against a different postId returns 422 rather than replaying the other post's response.

Only successful (2xx) responses are stored for replay. If the request throws or returns a non-2xx status the key is released, so the header protects the "request succeeded but the response was lost" case.

After an ambiguous failure (a 5xx or a network timeout), list the post's comments before retrying with the same key. Treat an empty result as inconclusive rather than as ...

Read only: no. Destructive: yes.

Inputs:

- `account_id` (`string`, required): (required)
- `message` (`string`, required): Reply text. On LinkedIn, mention with @[https://www.linkedin.com/in/vanity] (resolved to LinkedIn's live name, same rules and errors as post content) or @[Name](urn:li:person|organization:ID); a hand-written name must match LinkedIn's exactly or it renders as plain text. (required)
- `post_id` (`string`, required): Zernio post ID or platform-specific post ID. On LinkedIn, pass the post id exactly as the feed returns it (urn:li:share:<id> or urn:li:ugcPost:<id>); a bare number is read as an activity id (urn:li:activity:<n>). (required)
- `attachment_url` (`string`, optional): (Facebook only) URL of an image to attach, publishing a photo comment alongside the text. The URL must be publicly accessible so Meta can fetch it. Returns 400 for other platforms.
- `comment_id` (`string`, optional): Reply to specific comment (optional)
- `parent_cid` (`string`, optional): (Bluesky only) Parent content identifier
- `root_cid` (`string`, optional): (Bluesky only) Root post CID
- `root_uri` (`string`, optional): (Bluesky only) Root post URI

Example input: `{"account_id":"<account_id>","message":"<message>","post_id":"<post_id>"}`

### `zerniomcp_media_generate_upload_link`

Generate an upload URL for media files · Destructive

Generate a unique upload URL for the user to upload files via browser.

Use this when the user wants to include images or videos in their post.
The flow is:
1. Call this tool to get an upload URL
2. Ask the user to open the URL in their browser
3. User uploads files through the web interface
4. Call media_check_upload_status to get the uploaded file URLs
5. Use those URLs when creating the post with posts_create

Read only: no. Destructive: yes.

Inputs: none.

Example input: `{}`

### `zerniomcp_mentions_reply_to_mention`

Reply to a mention · Destructive

Reply to a mention

Reply to a mention of the connected account. Supported on Instagram only.

Two shapes, selected by whether `commentId` is present:

- **Comment mention** (someone @mentioned the account inside a comment): pass both
  `mediaId` and `commentId`. Instagram posts a reply under that comment.
- **Caption mention** (someone @mentioned the account in their media caption, so no
  comment exists): pass `mediaId` only. Instagram posts a comment on their media.

Story mentions are not supported by Instagram's API.

`GET /v1/inbox/mentions` returns Instagram mentions with the `mediaId` and `commentId`
to pass here.

Read only: no. Destructive: yes.

Inputs:

- `account_id` (`string`, required): The Instagram account ID (required)
- `media_id` (`string`, required): The ID of the media the account was mentioned in (required)
- `message` (`string`, required): The reply text (required)
- `comment_id` (`string`, optional): The mentioning comment's ID. Omit for a caption mention.

Example input: `{"account_id":"<account_id>","media_id":"<media_id>","message":"<message>"}`

### `zerniomcp_posts_bulk_upload_posts`

Bulk upload from CSV · Destructive

Bulk upload from CSV

Create multiple posts by uploading a CSV file. Use dryRun=true to validate without creating posts.

### Required columns

- `platforms`, `profiles`, and a schedule: one of `schedule_time`, a `schedule_time_<platform>` override, `publish_now=true`, `use_queue=true`, or `is_draft=true`.
- Content: at least one of `post_content`, `title`, or `media_urls`.

### Aliases

A handful of columns accept the JSON field name from POST /v1/posts, since integrators infer the CSV shape from that endpoint's body. When both are present the real CSV column wins, unless it is blank for that row, in which case the alias value is used.

- `content` aliases `post_content`
- `timezone` aliases `tz`
- `scheduledFor` aliases `schedule_time`
- `mediaUrls` aliases `media_urls`

### Per-platform overrides

Three dynamic column prefixes, one column per platform (e.g. `schedule_time_instagram`, `custom_content_tiktok`, `custom_media_youtube`):

- `schedule_time_<platform>`
- `custom_content_<platform>`
- ...

Args:
    dry_run

Read only: no. Destructive: yes.

Inputs:

- `dry_run` (`boolean`, optional): No description. Default: `false`.

Example input: `{}`

### `zerniomcp_posts_create`

Create a social media post (draft, scheduled, or immediate) · Destructive

Create a social media post. Can be saved as DRAFT, SCHEDULED, or PUBLISHED immediately.

⚠️ IMPORTANT - Choose the correct mode based on user intent:

**DRAFT MODE (is_draft=True)**
Use when user says: "draft", "borrador", "save for later", "don't publish", "save it", "guardar"
→ Post is saved but NOT published and NOT scheduled. User can edit it later.

**IMMEDIATE MODE (publish_now=True)**
Use when user says: "publish now", "post now", "publica ya", "immediately", "right now", "ahora"
→ Post goes live IMMEDIATELY.

**SCHEDULED MODE (default)**
Use when user says: "schedule", "programar", "in X minutes/hours", "at 3pm", "tomorrow"
→ Post is scheduled for future publication. Use schedule_minutes to set the delay.

⚠️ MULTI-ACCOUNT USERS (agencies, multi-client setups):
If the user has more than one account on the target platform, you MUST pass
`account_id`. Call `accounts_list` (or `profiles_list` then `accounts_list`)
first to discover the right ID. If you omit account_id when multiple accounts
exist, the tool returns an error listing the candidates - use it to retry.

Examples:
- "Create a draft tweet" → is_draft=True
- "Post this to Twitter now" → publish_now=True
- "Schedule a LinkedIn post for 2 hours from now" → schedule_minutes=120
- "Post this to Acme's Twitter" → call accounts_list, find Acme's twitter ID, pass account_id

Read only: no. Destructive: yes.

Inputs:

- `content` (`string`, required): The post text/content (required)
- `platform` (`string`, required): Target platform: twitter, instagram, linkedin, tiktok, bluesky, facebook, youtube, pinterest, threads (required)
- `account_id` (`string`, optional): Specific account ID to post from. REQUIRED when the user has multiple accounts on this platform. Call accounts_list first to find IDs. Leave empty only if there is exactly one account for this platform. Default: . Default: ``.
- `is_draft` (`boolean`, optional): Set to True to save as DRAFT (not published, not scheduled). Use when user wants to save without publishing Default: False. Default: `false`.
- `media_urls` (`string`, optional): Comma-separated URLs of media files to attach (images, videos) Default: . Default: ``.
- `profile_id` (`string`, optional): Scope account resolution to one profile (e.g. one client in an agency setup). Use when account_id is unknown but the target profile is. Call profiles_list to find IDs. Default: . Default: ``.
- `publish_now` (`boolean`, optional): Set to True to publish IMMEDIATELY. Post goes live right now Default: False. Default: `false`.
- `schedule_minutes` (`integer`, optional): Minutes from now to schedule. Only used when is_draft=False AND publish_now=False Default: 60. Default: `0`.
- `title` (`string`, optional): Post title (required for YouTube, recommended for Pinterest) Default: . Default: ``.

Example input: `{"content":"<content>","platform":"twitter"}`

### `zerniomcp_posts_cross_post`

Post the same content to multiple platforms · Destructive

Post the same content to multiple platforms at once.

⚠️ IMPORTANT - Choose the correct mode based on user intent:

**DRAFT MODE (is_draft=True)**
Use when user says: "draft", "borrador", "save for later", "don't publish"
→ Posts are saved but NOT published. User can edit them later.

**IMMEDIATE MODE (publish_now=True)**
Use when user says: "publish now", "post now", "immediately"
→ Posts go live IMMEDIATELY on all platforms.

**SCHEDULED MODE (default)**
Use when user says: "schedule", "programar", "in X hours"
→ Posts are scheduled for 1 hour from now.

⚠️ MULTI-ACCOUNT USERS:
- To pick a specific account per platform, pass `account_ids` parallel to
  `platforms` (same order, comma-separated). Use empty string for a position
  to fall back to profile/auto-resolution.
- To target multiple accounts of the SAME platform in one call, repeat the
  platform: platforms='twitter,twitter', account_ids='acc_a,acc_b'.
- If you omit account_ids and the user has multiple accounts for any of the
  requested platforms, the tool errors with the candidate list - use it to retry.

Read only: no. Destructive: yes.

Inputs:

- `content` (`string`, required): The post text/content (required)
- `platforms` (`string`, required): Comma-separated list of platforms (e.g., 'twitter,linkedin,bluesky'). Repeat a platform to target multiple accounts of it: 'twitter,twitter'. (required)
- `account_ids` (`string`, optional): Comma-separated account IDs, parallel to `platforms`. Empty positions fall back to profile/auto-resolution. Required for multi-account users to disambiguate. Default: . Default: ``.
- `is_draft` (`boolean`, optional): Set to True to save as DRAFT (not published). Use when user wants to save without publishing Default: False. Default: `false`.
- `media_urls` (`string`, optional): Comma-separated URLs of media files to attach Default: . Default: ``.
- `profile_id` (`string`, optional): Scope auto-resolution to one profile when account_ids is empty. Default: . Default: ``.
- `publish_now` (`boolean`, optional): Set to True to publish IMMEDIATELY to all platforms Default: False. Default: `false`.

Example input: `{"content":"<content>","platforms":"twitter,linkedin,bluesky"}`

### `zerniomcp_posts_delete`

Delete a post · Destructive

Delete a post by ID.

Published posts cannot be deleted.

Read only: no. Destructive: yes.

Inputs:

- `post_id` (`string`, required): The post ID to delete (required)

Example input: `{"post_id":"<post_id>"}`

### `zerniomcp_posts_edit_post`

Edit published post · Destructive

Edit published post

Edit the text of an already-published post. Supported on X, Discord, Facebook, Reddit, LinkedIn, Telegram, Pinterest, Google Business Profile, YouTube, and Slack.

When a post was published to several accounts on the same platform, pass `accountId` to pick which account's copy to edit (without it the request returns 409). Each platform enforces its own rules.

**X**

- Connected X account must have an active X Premium subscription
- Must be within 1 hour of original publish time
- Maximum 5 edits per tweet (enforced by X)
- Threads cannot be edited, only single tweets
- X assigns a NEW post ID on edit, returned as `id`

**Discord**

- No time limit and no premium requirement
- The message ID is unchanged after the edit

**Facebook**

- Graph only permits editing a post that the same app created. This works on posts published through Zernio and is rejected for posts created in Meta Business Suite / Composer or by another tool
- Media cannot be swapped, only the message text
- ...

Read only: no. Destructive: yes.

Inputs:

- `content` (`string`, required): The new post text content (required)
- `platform` (`string`, required): The platform to edit the post on. (required). One of: `twitter`, `discord`, `facebook`, `reddit`, `linkedin`, `telegram`, `pinterest`, `googlebusiness`, `youtube`, `slack`.
- `post_id` (`string`, required): (required)
- `account_id` (`string`, optional): Which account's copy of the post to edit when the post was published to several accounts on the same platform. Required in that case.

Example input: `{"content":"<content>","platform":"twitter","post_id":"<post_id>"}`

### `zerniomcp_posts_publish_now`

Publish a post immediately · Destructive

Publish a post immediately to a platform. The post goes live right away.

Use this when user explicitly wants to publish NOW, not schedule for later.
This is a convenience wrapper around posts_create with publish_now=True.

⚠️ MULTI-ACCOUNT USERS: pass `account_id` when the user has more than one
account on this platform. Call `accounts_list` first to find the right ID.

Read only: no. Destructive: yes.

Inputs:

- `content` (`string`, required): The post text/content (required)
- `platform` (`string`, required): Target platform: twitter, instagram, linkedin, tiktok, bluesky, etc. (required)
- `account_id` (`string`, optional): Specific account ID. REQUIRED when the user has multiple accounts on this platform. Leave empty only if there is exactly one. Default: . Default: ``.
- `media_urls` (`string`, optional): Comma-separated URLs of media files to attach Default: . Default: ``.
- `profile_id` (`string`, optional): Scope auto-resolution to a single profile when account_id is unknown. Default: . Default: ``.

Example input: `{"content":"<content>","platform":"twitter"}`

### `zerniomcp_posts_retry`

Retry a failed post · Destructive

Retry publishing a failed post. Only works on posts with 'failed' status.

Read only: no. Destructive: yes.

Inputs:

- `post_id` (`string`, required): The ID of the failed post to retry (required)

Example input: `{"post_id":"<post_id>"}`

### `zerniomcp_posts_retry_all_failed`

Retry all failed posts · Destructive

Retry all failed posts at once.

Read only: no. Destructive: yes.

Inputs: none.

Example input: `{}`

### `zerniomcp_posts_unpublish_post`

Unpublish post · Destructive

Unpublish post

Deletes a published post from the specified platform. The post record in Zernio is kept but its status is updated to cancelled.
Not supported on Instagram, TikTok, or Snapchat. Threaded posts delete all items. YouTube deletion is permanent.

Read only: no. Destructive: yes.

Inputs:

- `platform` (`string`, required): The platform to delete the post from (required). One of: `threads`, `facebook`, `twitter`, `linkedin`, `youtube`, `pinterest`, `reddit`, `bluesky`, `googlebusiness`, `telegram`.
- `post_id` (`string`, required): (required)
- `account_id` (`string`, optional): Which account's copy to delete when the post was published to several accounts on this platform. Required in that case.

Example input: `{"platform":"twitter","post_id":"<post_id>"}`

### `zerniomcp_posts_update`

Update an existing post · Destructive

Update an existing post.

Only draft, scheduled, and failed posts can be updated.
Published posts cannot be modified.

Read only: no. Destructive: yes.

Inputs:

- `post_id` (`string`, required): The post ID to update (required)
- `content` (`string`, optional): New content (leave empty to keep current) Default: . Default: ``.
- `scheduled_for` (`string`, optional): New schedule time as ISO string (leave empty to keep current) Default: . Default: ``.
- `title` (`string`, optional): New title (leave empty to keep current) Default: . Default: ``.

Example input: `{"post_id":"<post_id>"}`

### `zerniomcp_profiles_create`

Create a new profile · Destructive

Create a new profile for grouping social accounts.

Read only: no. Destructive: yes.

Inputs:

- `name` (`string`, required): Profile name (required)
- `color` (`string`, optional): Optional hex color (e.g., '#4CAF50') Default: . Default: ``.
- `description` (`string`, optional): Optional description Default: . Default: ``.

Example input: `{"name":"<name>"}`

### `zerniomcp_profiles_delete`

Delete a profile · Destructive

Delete a profile. The profile must have no connected accounts.

Read only: no. Destructive: yes.

Inputs:

- `profile_id` (`string`, required): The profile ID to delete (required)

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_profiles_update`

Update an existing profile · Destructive

Update an existing profile. Only provided fields will be changed.

Read only: no. Destructive: yes.

Inputs:

- `profile_id` (`string`, required): The profile ID to update (required)
- `color` (`string`, optional): New hex color (leave empty to keep current) Default: . Default: ``.
- `description` (`string`, optional): New description (leave empty to keep current) Default: . Default: ``.
- `is_default` (`boolean`, optional): Set as default profile Default: False. Default: `false`.
- `name` (`string`, optional): New name (leave empty to keep current) Default: . Default: ``.

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_queue_create_queue_slot`

Create schedule · Destructive

Create schedule

Create an additional queue for a profile. The first queue created becomes the default.
Subsequent queues are non-default unless explicitly set.

Read only: no. Destructive: yes.

Inputs:

- `name` (`string`, required): Queue name (e.g., Evening Posts) (required)
- `profile_id` (`string`, required): Profile ID (required)
- `slots` (`string`, required): Each item is an object with keys: dayOfWeek (integer) - Day of week (0=Sunday, 6=Saturday); time (string) - Time in HH:mm format (24-hour) (required)
- `timezone` (`string`, required): IANA timezone (required)
- `active` (`boolean`, optional): No description. Default: `true`.

Example input: `{"name":"<name>","profile_id":"<profile_id>","slots":"<slots>","timezone":"<timezone>"}`

### `zerniomcp_queue_delete_queue_slot`

Delete schedule · Destructive

Delete schedule

Delete a queue from a profile. Pass queueId to delete a specific queue;
omit it to delete all queues for the profile.
If deleting the default queue, another queue will be promoted to default.

Read only: no. Destructive: yes.

Inputs:

- `profile_id` (`string`, required): (required)
- `queue_id` (`string`, optional): Queue ID to delete. Omit to delete all queues for the profile

Example input: `{"profile_id":"<profile_id>"}`

### `zerniomcp_queue_update_queue_slot`

Update schedule · Destructive

Update schedule

Create a new queue or update an existing one. Without queueId, creates/updates the default queue. With queueId, updates a specific queue. With setAsDefault=true, makes this queue the default for the profile.

Read only: no. Destructive: yes.

Inputs:

- `profile_id` (`string`, required): (required)
- `slots` (`string`, required): Each item is an object with keys: dayOfWeek (integer) - Day of week (0=Sunday, 6=Saturday); time (string) - Time in HH:mm format (24-hour) (required)
- `timezone` (`string`, required): (required)
- `active` (`boolean`, optional): No description. Default: `true`.
- `name` (`string`, optional): Queue name
- `queue_id` (`string`, optional): Queue ID to update (optional)
- `reshuffle_existing` (`boolean`, optional): Whether to reschedule existing queued posts to match new slots. Default: `false`.
- `set_as_default` (`string`, optional): Make this queue the default

Example input: `{"profile_id":"<profile_id>","slots":"<slots>","timezone":"<timezone>"}`

### `zerniomcp_support_ask`

Ask Zernio's Support Assistant to diagnose a problem (paid) · Destructive

Ask Zernio's Support Assistant to diagnose a problem with the user's Zernio workspace.

PAID: typically $0.10 to $0.50 per question, never more than max_cost_usd. Failed questions are free.

Use this once per unexplained Zernio API error, after docs_search did not explain it. Never loop or retry: identical arguments return the same run, not a new one. For a follow-up, pass thread_id from the previous answer.

Requires an unrestricted Zernio API key. OAuth connectors (claude.ai, ChatGPT) and restricted or profile-scoped keys cannot use it.

If the answer is not ready within wait_seconds, the result carries the runId; fetch it later with support_assistant_get_support_answer.

Read only: no. Destructive: yes.

Inputs:

- `question` (`string`, required): The problem to diagnose, with the error message and what was attempted (required)
- `account_id` (`string`, optional): Account the assistant should look at. Default: ``.
- `max_cost_usd` (`number`, optional): Cost cap in USD for this question (0.5 to 3) Default: 1.0. Default: `1`.
- `post_id` (`string`, optional): Post the assistant should look at. Default: ``.
- `profile_id` (`string`, optional): Profile the assistant should look at. Default: ``.
- `thread_id` (`string`, optional): Continue an earlier conversation (threadId from a previous answer). Default: ``.
- `wait_seconds` (`integer`, optional): How long to wait for the answer before returning the run id (0 to 120) Default: 45. Default: `45`.

Example input: `{"question":"<question>"}`

### `zerniomcp_validate_media`

Validate media URL · Destructive

Validate media URL

Check if a media URL is accessible and return metadata (content type, file size) plus per-platform size limit comparisons.

Performs a HEAD request (with GET fallback) to detect content type and size. Rejects private/localhost URLs for SSRF protection.

Platform limits are sourced from each platform's actual upload constraints.

Read only: no. Destructive: yes.

Inputs:

- `url` (`string`, required): Public media URL to validate (required)

Example input: `{"url":"<url>"}`

### `zerniomcp_validate_post`

Validate post content · Destructive

Validate post content

Dry-run the full post validation pipeline without publishing. Catches issues like missing media for Instagram/TikTok/YouTube, hashtag limits, invalid thread formats, Facebook Reel requirements, and character limit violations.

Accepts the same body as POST /v1/posts. Does NOT validate accounts, process media, or track usage. Account lookups are limit-only: a twitter accountId is resolved, scoped to the caller, only to pick the 280 vs 25000 character limit. Missing, foreign, or invalid ids fall back to 280 and never error.

Returns errors for failures and warnings for near-limit content (>90% of character limit).

Read only: no. Destructive: yes.

Inputs:

- `platforms` (`string`, required): Target platforms (same format as POST /v1/posts) Each item is an object with keys: platform (one of: twitter, instagram, tiktok, youtube, facebook, linkedin, bluesky, threads, reddit, pinterest, telegram, snapchat, googlebusiness, discord, slack; required); accountId (string) - Account to validate against. For twitter, resolves X Premium status to apply the 25000 character limit instead of 280.; customContent (string); platformSpecificData (object); customMedia (list of objects with keys type, url, fileId, accountId, title, altText, filename, size, mimeType, thumbnail, instagramThumbnail, subtitles, tiktokProcessed) (required)
- `content` (`string`, optional): Post text content
- `media_items` (`string`, optional): Root media items shared across platforms Each item is an object with keys: type (one of: image, video, gif, document) - Optional when the item names a connected file (`fileId`): inferred from the file's MIME type.; url (string) - A media item with a null, missing or empty url is dropped, unless it names a connected file with `fileId` + `accountId`.; fileId (string) - A file of a connected Google Drive or OneDrive account (see the Files endpoints). Requires `accountId`. The file is copied into Zernio media storage when the ...; accountId (string) - The connected Google or Microsoft account the `fileId` belongs to (must have the `files` area). Required with `fileId`.; title (string) - Optional title for the media item. Used as the document title for LinkedIn PDF/carousel posts. If omitted, falls back to the post title, then the filename.; altText (string) - Accessibility alternative text for an image. Set once per image; the same value is sent to each selected platform. - Applied on: Instagram (feed images only, ...; filename (string); size (integer) - Optional file size in bytes; mimeType (string) - Optional MIME type (e.g. image/jpeg, video/mp4); thumbnail (string) - Optional custom thumbnail/cover image URL for videos. Supported for Facebook video posts, Facebook Reels, YouTube videos and Shorts, and LinkedIn video posts. ...; instagramThumbnail (string) - Custom cover image URL for Instagram Reels. Can also be set via platformSpecificData.instagramThumbnail or platformSpecificData.reelCover. Resolution order: ...; subtitles (list of objects with keys url, language) - Subtitle (closed caption) files for a video item, in SRT or WebVTT. Each language may appear once per video. Zernio downloads each file when the post is ...; tiktokProcessed (boolean) - Internal flag indicating the image was resized for TikTok

Example input: `{"platforms":"twitter,linkedin,bluesky"}`

### `zerniomcp_validate_post_length`

Validate character count · Destructive

Validate character count

Check weighted character count per platform and whether the text is within each platform's limit.

Returns counts and limits for all 15 supported platform variants.

### Counting

- X uses weighted counting (URLs = 23 chars via t.co, emojis = 2 chars).
- All other platforms use plain character length.

### X rows

X returns two rows, and this endpoint cannot tell you which one applies to you: it takes only `text`, so it never resolves an account.

- `twitter` (280) is the free tier limit.
- `twitterPremium` (25000) applies only when the target X account has a paid X subscription. Publishing enforces 280 instead for any post carrying a poll. This endpoint has no poll input, so the `twitterPremium` row always shows 25000.

A free account trusting the `twitterPremium` row can pass validation here and still fail at publish time, where the account's real limit is enforced.

### Per-account limits

To validate against the per-account limit, use `POST /v1/tools/validate/post` instead. It ...

Read only: no. Destructive: yes.

Inputs:

- `text` (`string`, required): The post text to check (required)

Example input: `{"text":"<text>"}`

## Related

Other Marketing connectors ([all 98](/agentkit/connectors/?category=marketing)):

- [HubSpot](/agentkit/connectors/hubspot/): Scalekit connector, OAuth, 457 tools
- [Mailchimp](/agentkit/connectors/mailchimp/): Scalekit connector, OAuth, 73 tools
- [Twitter / X](/agentkit/connectors/twitteroauth/): Scalekit connector, OAuth, 92 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
