Docs menu · Tools

MCP for AI agents

MCP tools

The 19 tools PostNinja's MCP server gives your agent. Parameter tables come straight from the server's own input schemas. Results are a short summary followed by JSON; the same data is in structuredContent.result.

View as Markdown

All tools

ToolWhat it doesAccess
list_accountsList accountsread
get_workspaceGet workspace settingsread
list_profilesList profilesread
list_templatesList post templatesread
list_postsList postsread
get_postGet a postread
create_postCreate or schedule a postwrite
update_postChange a postwrite
delete_postDelete a postwrite
approve_postApprove a postwrite
request_changesAsk for changes to a postwrite
get_queueGet the posting queueread
update_queueChange the posting queuewrite
get_analyticsGet analyticsread
list_commentsList commentsread
reply_to_commentReply to a commentwrite
list_messagesList direct messagesread
reply_to_messageReply to a direct messagewrite
upload_imageUpload an imagewrite

A typical flow: list_accounts → (get_workspace / list_profiles for the brand voice) → upload_image if there's a photo → create_post → later, get_post to check it went out.

list_accounts

List accounts. The connected social accounts you can post to (every platform), with their ids.

Read-only. Needs posts:read.

When to use it: Call it first in almost every conversation: other tools take account ids. status: needs_reconnect means the account must be reconnected in the app before it can post. profileId says which profile (client or brand) an account belongs to.

Parameters

None.

Example arguments
{}
Example result (the text your agent receives)
Connected accounts:

[
  {
    "id": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
    "platform": "x",
    "username": "acme",
    "displayName": "Acme",
    "status": "active",
    "profileId": null
  },
  {
    "id": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
    "platform": "instagram",
    "username": "acme.studio",
    "displayName": "Acme Studio",
    "status": "active",
    "profileId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d"
  },
  {
    "id": "c41d9e7a-2b3c-4d5e-8f60-718293a4b5c6",
    "platform": "tiktok",
    "username": "acmehq",
    "displayName": "Acme",
    "status": "needs_reconnect",
    "profileId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d"
  }
]

get_workspace

Get workspace settings. The workspace's name, its brand voice (how posts should sound) and whether members' posts need approval.

Read-only. Needs posts:read.

When to use it: Before writing posts, to match the workspace's brand voice, and to know whether members' posts need approval.

Parameters

None.

Example arguments
{}
Example result (the text your agent receives)
Workspace:

{
  "name": "Acme",
  "approvalRequired": true,
  "brandVoice": "Friendly and plain-spoken. Short sentences, no jargon, one emoji at most."
}

list_profiles

List profiles. The profiles in this workspace (usually one per client or brand: a set of connected accounts), each with its own brand voice. Use a profile's brand voice when writing for its accounts, and its id (profileId) to filter list_posts.

Read-only. Needs posts:read.

When to use it: When the workspace runs several brands or clients. Write in a profile's brandVoice when posting to its accounts (it wins over the workspace's), and pass its id as profileId to list_posts, get_analytics or update_queue. Profiles are part of the Pro and Agency plans; on Personal the list is empty.

Parameters

None.

Example arguments
{}
Example result (the text your agent receives)
Profiles:

[
  {
    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    "name": "Fern & Clay",
    "color": "green",
    "brandVoice": "Warm, handmade, a little playful. Talk about the makers.",
    "accountIds": [
      "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "c41d9e7a-2b3c-4d5e-8f60-718293a4b5c6"
    ],
    "reviewLink": true
  }
]

list_templates

List post templates. Ready-made post templates (the built-in library and the team's saved ones). Each has {key} blanks described in fields: replace them with real details (drop lines whose optional blank you leave empty), then pass the text to create_post.

Read-only. Needs posts:read.

When to use it: When the user wants a post of a known kind (a sale, a launch, a restock, an event) or asks for ideas. Fill every {key} blank with real details, drop lines whose optional blank you leave empty, then pass the text to create_post.

Parameters

  • categorystring

    One of: promote (Sales and offers), launch (Launches and news), engage (Engagement), educate (Tips and how-tos), proof (Social proof), behind (Behind the scenes), events (Events and seasons), story (Stories and personal brand), community (Community), creator (Creators and influencers), or saved (the team's own).

  • searchstring

    Words to look for in the name and description. Up to 100 characters.

  • limitinteger

    1 to 100. Default 30.

Example arguments
{
  "category": "launch",
  "search": "stock",
  "limit": 5
}
Example result (the text your agent receives)
Templates:

[
  {
    "id": "t_back-in-stock",
    "name": "Back in stock",
    "category": "launch",
    "description": "Let people know a popular item is available again.",
    "fields": [
      {
        "key": "product",
        "label": "Product",
        "example": "Our speckled stoneware mugs",
        "optional": false
      },
      {
        "key": "quantity",
        "label": "How many",
        "example": "We made 40 this round, and they went fast last time.",
        "optional": true
      },
      {
        "key": "link",
        "label": "Link",
        "example": "fernandclay.com/mugs",
        "optional": true
      }
    ],
    "content": "Back in stock: {product} 🙌\n\n{quantity}\n\nThank you to everyone who asked us to bring them back. If you missed out before, now's your chance.\n{link}\n\n#BackInStock",
    "platformContent": {}
  }
]

list_posts

List posts. Posts in this workspace, newest scheduled time first. Filter by time range, status or profile.

Read-only. Needs posts:read.

When to use it: To see what's scheduled, find drafts, check what failed, or review posts waiting for approval (status: awaiting_approval). Times are compared with each post's scheduled time.

Parameters

  • fromstring (ISO 8601)

    Only posts scheduled at or after this time.

  • tostring (ISO 8601)

    Only posts scheduled before this time.

  • statusstring

    awaiting_approval: posts waiting for an owner or admin to approve them. One of draft, awaiting_approval, scheduled, publishing, published, partial, failed.

  • profileIdstring (uuid)

    Only posts going to at least one of this profile's accounts (see list_profiles).

  • limitinteger

    1 to 100. Default 50.

Example arguments
{
  "from": "2026-10-12T00:00:00Z",
  "to": "2026-10-19T00:00:00Z",
  "status": "scheduled",
  "limit": 20
}
Example result (the text your agent receives)
Posts:

[
  {
    "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
    "status": "scheduled",
    "content": "We just shipped dark mode. Here's the full story.",
    "platformContent": {
      "x": "Dark mode is here 🌙"
    },
    "xThread": [],
    "xThreadMedia": [],
    "instagramFormat": "post",
    "tiktok": {},
    "firstComment": null,
    "youtubeTitle": null,
    "youtubeThumbnail": null,
    "queued": false,
    "scheduledAt": "2026-10-12T09:00:00.000Z",
    "publishedAt": null,
    "createdAt": "2026-10-06T14:21:07.512Z",
    "media": [
      {
        "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
        "kind": "image",
        "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
        "previewUrl": null
      }
    ],
    "targets": [
      {
        "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
        "platform": "x",
        "username": "acme",
        "status": "pending",
        "url": null,
        "error": null
      },
      {
        "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
        "platform": "instagram",
        "username": "acme.studio",
        "status": "pending",
        "url": null,
        "error": null
      }
    ]
  }
]

get_post

Get a post. One post with its per-account status, links to the published posts, any errors, and its approval history.

Read-only. Needs posts:read.

When to use it: To check whether a post went out: each target has its own status, a link (url) once published, or the reason it failed (error). Also returns the approval history.

Parameters

  • postIdstring (uuid)required

    The post's id.

Example arguments
{
  "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9"
}
Example result (the text your agent receives)
Post:

{
  "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "status": "awaiting_approval",
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is here 🌙"
  },
  "xThread": [],
  "xThreadMedia": [],
  "instagramFormat": "post",
  "tiktok": {},
  "firstComment": null,
  "youtubeTitle": null,
  "youtubeThumbnail": null,
  "queued": false,
  "scheduledAt": "2026-10-12T09:00:00.000Z",
  "publishedAt": null,
  "createdAt": "2026-10-06T14:21:07.512Z",
  "media": [
    {
      "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
      "kind": "image",
      "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
      "previewUrl": null
    }
  ],
  "targets": [
    {
      "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
      "platform": "x",
      "username": "acme",
      "status": "pending",
      "url": null,
      "error": null
    },
    {
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "status": "pending",
      "url": null,
      "error": null
    }
  ],
  "approvalHistory": [
    {
      "action": "submitted",
      "by": "Sam Lee",
      "note": null,
      "at": "2026-10-06T10:02:11.000Z"
    },
    {
      "action": "changes_requested",
      "by": "Alex Kim",
      "note": "Use the photo from Saturday.",
      "at": "2026-10-06T11:40:52.000Z"
    }
  ]
}

create_post

Create or schedule a post. Creates a post for one or more accounts. Set scheduledAt to schedule it, queue to put it in the next free queue slot, publishNow to post right away, or draft to save it without scheduling. Every platform's rules are checked (X: 280 characters, up to 4 photos or 1 video; Instagram: 1-10 photos/videos required, 2,200-character caption; Threads: 500 characters; TikTok: a video or photos; YouTube Shorts: one vertical video up to 3 minutes; Pinterest: photos or a video, 800-character description).

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To schedule (scheduledAt), publish right away (publishNow: true), queue (queue: true) or save a draft (draft: true). Every selected platform's rules are checked first, so an error message tells you exactly what to fix. Give platforms their own text with platformContent.

Parameters

  • contentstring

    The post's text. Used on every platform that doesn't have its own text in platformContent. Up to 10,000 characters.

  • platformContentobject

    Optional text per platform that replaces content there, e.g. {"x": "Short version"}. Keys: x, instagram, threads, facebook, tiktok, youtube, pinterest.

  • xThread(string or object)[]

    X only: follow-up posts, in order, that turn the X version into a thread. The first post is the X text (platformContent.x or content). Each is a string, or { "text": "...", "mediaIds": [...] } to give that post its own photos or video. Each is up to 280 characters and costs credits like any X post. Up to 24 items.

  • xThread[].textstringrequired

    Up to 10,000 characters.

  • xThread[].mediaIdsstring (uuid)[]

    This post's own photos (up to 4) or one video. Up to 4 items.

  • instagramFormatstring

    Instagram only: "post" (feed photo, carousel or Reel, the default) or "story" (exactly one photo or video; captions are not shown on Stories). Default "post".

  • tiktokobject

    TikTok only: who can see the post, which interactions to allow, and commercial content disclosure.

  • tiktok.privacystring

    Who can see the TikTok post. Left out: public if the account allows it. Ask the user rather than guessing. One of PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY.

  • tiktok.allowCommentsboolean

    Allow comments. Default true.

  • tiktok.allowDuetboolean

    Allow Duet (videos only). Default true.

  • tiktok.allowStitchboolean

    Allow Stitch (videos only). Default true.

  • tiktok.yourBrandboolean

    Commercial content promoting the user's own business. TikTok labels it "Promotional content".

  • tiktok.brandedContentboolean

    Paid partnership promoting a third party. TikTok labels it "Paid partnership"; it can't be private (SELF_ONLY).

  • tiktok.coverTimestampMsinteger

    Videos: which moment of the video is the cover, in milliseconds from the start.

  • firstCommentstring

    Optional first comment, posted under the post right after it goes out, where the platform allows it (e.g. links or hashtags). Empty removes it. Up to 2,200 characters.

  • youtubeTitlestring

    YouTube only: the video title (up to 100 characters). Left out: the first line of the text.

  • youtubeThumbnailIdstring (uuid) or null

    YouTube only: an uploaded photo (under 2 MB) to use as the video's custom thumbnail. null removes it.

  • queueboolean

    Put the post in the next free slot of the posting queue (the accounts' profile's slots, else the workspace's) instead of at scheduledAt.

  • accountIdsstring (uuid)[]

    Accounts to post to (from list accounts). Up to 200 items.

  • mediaIdsstring (uuid)[]

    Uploaded media, in display order (from upload media). Up to 10 items.

  • scheduledAtstring (ISO 8601)

    When to publish, ISO 8601 with a time zone, e.g. 2026-10-05T09:00:00Z.

  • publishNowboolean

    Publish as soon as possible instead of at scheduledAt.

  • draftboolean

    Save as a draft without scheduling it.

Example arguments
{
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is here 🌙"
  },
  "accountIds": [
    "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
    "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40"
  ],
  "mediaIds": [
    "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"
  ],
  "scheduledAt": "2026-10-12T09:00:00Z"
}
Example result (the text your agent receives)
Created post 0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9. Scheduled for 2026-10-12T09:00:00.000Z.

{
  "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "status": "scheduled",
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is here 🌙"
  },
  "xThread": [],
  "xThreadMedia": [],
  "instagramFormat": "post",
  "tiktok": {},
  "firstComment": null,
  "youtubeTitle": null,
  "youtubeThumbnail": null,
  "queued": false,
  "scheduledAt": "2026-10-12T09:00:00.000Z",
  "publishedAt": null,
  "createdAt": "2026-10-06T14:21:07.512Z",
  "media": [
    {
      "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
      "kind": "image",
      "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
      "previewUrl": null
    }
  ],
  "targets": [
    {
      "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
      "platform": "x",
      "username": "acme",
      "status": "pending",
      "url": null,
      "error": null
    },
    {
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "status": "pending",
      "url": null,
      "error": null
    }
  ]
}

Note: When approvals are on and the person who signed in is a member, the summary says the post was sent for approval and status is awaiting_approval.

Note: X posts use credits; if the workspace can't afford them the call fails with a message saying how many are needed.

update_post

Change a post. Changes a draft, scheduled or waiting-for-approval post. Only the fields you pass change; the rest stay as they are. When an owner or admin (or an API key) schedules a post that was waiting for approval, that approves it.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To change the text, accounts, media, time or options of a draft, scheduled or waiting post. Only the fields you pass change. Send scheduledAt to move a post, draft: true to unschedule it, or queue: true to put it in the queue. Posts that already went out can't be changed.

Parameters

  • contentstring

    The post's text. Used on every platform that doesn't have its own text in platformContent. Up to 10,000 characters.

  • platformContentobject

    Optional text per platform that replaces content there, e.g. {"x": "Short version"}. Keys: x, instagram, threads, facebook, tiktok, youtube, pinterest.

  • xThread(string or object)[]

    X only: follow-up posts, in order, that turn the X version into a thread. The first post is the X text (platformContent.x or content). Each is a string, or { "text": "...", "mediaIds": [...] } to give that post its own photos or video. Each is up to 280 characters and costs credits like any X post. Up to 24 items.

  • xThread[].textstringrequired

    Up to 10,000 characters.

  • xThread[].mediaIdsstring (uuid)[]

    This post's own photos (up to 4) or one video. Up to 4 items.

  • instagramFormatstring

    Instagram only: "post" (feed photo, carousel or Reel, the default) or "story" (exactly one photo or video; captions are not shown on Stories).

  • tiktokobject

    TikTok only: who can see the post, which interactions to allow, and commercial content disclosure.

  • tiktok.privacystring

    Who can see the TikTok post. Left out: public if the account allows it. Ask the user rather than guessing. One of PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY.

  • tiktok.allowCommentsboolean

    Allow comments. Default true.

  • tiktok.allowDuetboolean

    Allow Duet (videos only). Default true.

  • tiktok.allowStitchboolean

    Allow Stitch (videos only). Default true.

  • tiktok.yourBrandboolean

    Commercial content promoting the user's own business. TikTok labels it "Promotional content".

  • tiktok.brandedContentboolean

    Paid partnership promoting a third party. TikTok labels it "Paid partnership"; it can't be private (SELF_ONLY).

  • tiktok.coverTimestampMsinteger

    Videos: which moment of the video is the cover, in milliseconds from the start.

  • firstCommentstring

    Optional first comment, posted under the post right after it goes out, where the platform allows it (e.g. links or hashtags). Empty removes it. Up to 2,200 characters.

  • youtubeTitlestring

    YouTube only: the video title (up to 100 characters). Left out: the first line of the text.

  • youtubeThumbnailIdstring (uuid) or null

    YouTube only: an uploaded photo (under 2 MB) to use as the video's custom thumbnail. null removes it.

  • queueboolean

    Put the post in the next free slot of the posting queue (the accounts' profile's slots, else the workspace's) instead of at scheduledAt.

  • accountIdsstring (uuid)[]

    Accounts to post to (from list accounts). Up to 200 items.

  • mediaIdsstring (uuid)[]

    Uploaded media, in display order (from upload media). Up to 10 items.

  • scheduledAtstring (ISO 8601)

    When to publish, ISO 8601 with a time zone, e.g. 2026-10-05T09:00:00Z.

  • publishNowboolean

    Publish as soon as possible instead of at scheduledAt.

  • draftboolean

    Save as a draft without scheduling it.

  • postIdstring (uuid)required

    The post's id.

Example arguments
{
  "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "scheduledAt": "2026-10-13T09:00:00Z",
  "platformContent": {
    "x": "Dark mode is finally here 🌙"
  }
}
Example result (the text your agent receives)
Updated post 0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9.

{
  "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "status": "scheduled",
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is finally here 🌙"
  },
  "xThread": [],
  "xThreadMedia": [],
  "instagramFormat": "post",
  "tiktok": {},
  "firstComment": null,
  "youtubeTitle": null,
  "youtubeThumbnail": null,
  "queued": false,
  "scheduledAt": "2026-10-13T09:00:00.000Z",
  "publishedAt": null,
  "createdAt": "2026-10-06T14:21:07.512Z",
  "media": [
    {
      "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
      "kind": "image",
      "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
      "previewUrl": null
    }
  ],
  "targets": [
    {
      "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
      "platform": "x",
      "username": "acme",
      "status": "pending",
      "url": null,
      "error": null
    },
    {
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "status": "pending",
      "url": null,
      "error": null
    }
  ]
}

delete_post

Delete a post. Deletes a post (draft, scheduled, waiting or sent). A post that was already published stays live on the platform; it's only removed from PostNinja.

Changes data. Needs posts:write (or a Read & write API key). Destructive: it can't be undone.

When to use it: To cancel a draft or scheduled post. Deleting a post that already went out only removes it from PostNinja; it stays live on the platforms. Credits held for accounts it hadn't reached yet are given back.

Parameters

  • postIdstring (uuid)required

    The post's id.

Example arguments
{
  "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9"
}
Example result (the text your agent receives)
Deleted post 0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9.

approve_post

Approve a post. Approves a post waiting for approval (status awaiting_approval), so it's scheduled for its time, or goes out now if that time has passed. Only the workspace owner or an admin can approve.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: When an owner or admin asks you to review what the team submitted: list posts with status: awaiting_approval, show them, and approve the ones they're happy with. A post whose time has passed goes out right away.

Parameters

  • postIdstring (uuid)required

    The post's id.

  • notestring

    Optional note for the author. Up to 1,000 characters.

Example arguments
{
  "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "note": "Looks great."
}
Example result (the text your agent receives)
Approved post 0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9.

{
  "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "status": "scheduled",
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is here 🌙"
  },
  "xThread": [],
  "xThreadMedia": [],
  "instagramFormat": "post",
  "tiktok": {},
  "firstComment": null,
  "youtubeTitle": null,
  "youtubeThumbnail": null,
  "queued": false,
  "scheduledAt": "2026-10-12T09:00:00.000Z",
  "publishedAt": null,
  "createdAt": "2026-10-06T14:21:07.512Z",
  "media": [
    {
      "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
      "kind": "image",
      "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
      "previewUrl": null
    }
  ],
  "targets": [
    {
      "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
      "platform": "x",
      "username": "acme",
      "status": "pending",
      "url": null,
      "error": null
    },
    {
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "status": "pending",
      "url": null,
      "error": null
    }
  ],
  "approvalHistory": [
    {
      "action": "submitted",
      "by": "Sam Lee",
      "note": null,
      "at": "2026-10-06T10:02:11.000Z"
    },
    {
      "action": "approved",
      "by": "Alex Kim",
      "note": "Looks great.",
      "at": "2026-10-06T12:01:09.000Z"
    }
  ]
}

request_changes

Ask for changes to a post. Sends a post waiting for approval back to its author's drafts with a note saying what to change; the author gets it by email. Only the workspace owner or an admin can do this.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To send a waiting post back to its author's drafts. The note is required and emailed to the author, so say exactly what to change.

Parameters

  • postIdstring (uuid)required

    The post's id.

  • notestringrequired

    What needs changing. The author gets it by email. Up to 1,000 characters.

Example arguments
{
  "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "note": "Use the photo from Saturday and mention free delivery."
}
Example result (the text your agent receives)
Sent post 0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9 back to its author.

{
  "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
  "status": "draft",
  "content": "We just shipped dark mode. Here's the full story.",
  "platformContent": {
    "x": "Dark mode is here 🌙"
  },
  "xThread": [],
  "xThreadMedia": [],
  "instagramFormat": "post",
  "tiktok": {},
  "firstComment": null,
  "youtubeTitle": null,
  "youtubeThumbnail": null,
  "queued": false,
  "scheduledAt": "2026-10-12T09:00:00.000Z",
  "publishedAt": null,
  "createdAt": "2026-10-06T14:21:07.512Z",
  "media": [
    {
      "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
      "kind": "image",
      "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg",
      "previewUrl": null
    }
  ],
  "targets": [
    {
      "accountId": "3f2c8a10-6d1e-4b7a-9c55-1e2f3a4b5c6d",
      "platform": "x",
      "username": "acme",
      "status": "pending",
      "url": null,
      "error": null
    },
    {
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "status": "pending",
      "url": null,
      "error": null
    }
  ],
  "approvalHistory": [
    {
      "action": "submitted",
      "by": "Sam Lee",
      "note": null,
      "at": "2026-10-06T10:02:11.000Z"
    },
    {
      "action": "changes_requested",
      "by": "Alex Kim",
      "note": "Use the photo from Saturday.",
      "at": "2026-10-06T11:40:52.000Z"
    }
  ]
}

get_queue

Get the posting queue. The posting queue's weekly time slots (weekday 0 = Monday … 6 = Sunday, 24-hour HH:MM in the queue's time zone): the workspace's (profileId null) and each profile's own. create_post with queue: true takes the next free slot of the accounts' profile, else the workspace's.

Read-only. Needs posts:read.

When to use it: Before queueing posts, to see the weekly slots and their time zone. profileId: null slots are the workspace's; a profile with its own slots uses those instead.

Parameters

None.

Example arguments
{}
Example result (the text your agent receives)
Posting queue:

{
  "timeZone": "Europe/London",
  "slots": [
    {
      "weekday": 0,
      "time": "09:00",
      "profileId": null
    },
    {
      "weekday": 2,
      "time": "09:00",
      "profileId": null
    },
    {
      "weekday": 4,
      "time": "12:30",
      "profileId": null
    },
    {
      "weekday": 1,
      "time": "18:00",
      "profileId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d"
    }
  ]
}

update_queue

Change the posting queue. Replaces one set of weekly queue slots: a profile's (profileId) or the workspace's (profileId null). Send the complete new list; an empty list removes a profile's own slots so it uses the workspace's. Only the workspace owner or an admin can do this. Posts already queued keep their times.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To set up or change posting times. Send the complete list for one set (it replaces the old one). An empty list removes a profile's own slots, so it falls back to the workspace's. Posts already queued keep their times.

Parameters

  • profileIdstring (uuid) or null

    Whose slots to replace: a profile's id (from list_profiles), or null for the workspace's own slots. Default null.

  • slotsobject[]required

    The complete new list of weekly slots. An empty list removes them (a profile then uses the workspace's slots). Up to 100 items.

  • slots[].weekdayintegerrequired

    0 = Monday … 6 = Sunday. 0 to 6.

  • slots[].timestringrequired

    24-hour HH:MM in the queue's time zone.

Example arguments
{
  "profileId": null,
  "slots": [
    {
      "weekday": 0,
      "time": "09:00"
    },
    {
      "weekday": 2,
      "time": "09:00"
    },
    {
      "weekday": 4,
      "time": "12:30"
    }
  ]
}
Example result (the text your agent receives)
Updated the posting queue.

{
  "timeZone": "Europe/London",
  "slots": [
    {
      "weekday": 0,
      "time": "09:00",
      "profileId": null
    },
    {
      "weekday": 2,
      "time": "09:00",
      "profileId": null
    },
    {
      "weekday": 4,
      "time": "12:30",
      "profileId": null
    }
  ]
}

get_analytics

Get analytics. Published posts' stats and follower growth for a date range (UTC days, default the last 30), compared with the period before: headline numbers, one row per account and the top posts. Filter by profile, account or platform.

Read-only. Needs posts:read.

When to use it: For reports: how posts did over a period, which accounts grew, and the top posts. Numbers come from stats already synced, so this costs nothing and is fast. Compare kpis with kpis.previous (the same number of days before).

Parameters

  • fromstring

    First day, YYYY-MM-DD (send with to).

  • tostring

    Last day, YYYY-MM-DD (send with from).

  • profileIdstring (uuid)

    Only this profile's accounts (see list_profiles).

  • accountIdstring (uuid)

    Only this account (see list_accounts).

  • platformstring

    Only this platform, e.g. instagram or youtube.

Example arguments
{
  "from": "2026-09-07",
  "to": "2026-10-06"
}
Example result (the text your agent receives)
Analytics:

{
  "range": {
    "from": "2026-09-07",
    "to": "2026-10-06",
    "days": 30,
    "timeZone": "UTC"
  },
  "previousRange": {
    "from": "2026-08-08",
    "to": "2026-09-06"
  },
  "kpis": {
    "postsPublished": 12,
    "postsWithStats": 11,
    "impressions": 48210,
    "engagements": 2391,
    "likes": 1874,
    "reposts": 143,
    "replies": 262,
    "quotes": 0,
    "bookmarks": 112,
    "engagementRate": 0.0496,
    "followers": 18422,
    "followerGrowth": 611,
    "bestPlatform": {
      "platform": "instagram",
      "engagementRate": 0.0712,
      "posts": 6
    },
    "previous": {
      "postsPublished": 12,
      "postsWithStats": 11,
      "impressions": 39120,
      "engagements": 1830,
      "likes": 1874,
      "reposts": 143,
      "replies": 262,
      "quotes": 0,
      "bookmarks": 112,
      "engagementRate": 0.0468,
      "followerGrowth": 402
    }
  },
  "accounts": [
    {
      "id": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "platform": "instagram",
      "username": "acme.studio",
      "displayName": "Acme Studio",
      "profileId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
      "followers": 12904,
      "followersCountedOn": "2026-10-06",
      "followerGrowth": 488,
      "postsPublished": 12,
      "postsWithStats": 11,
      "impressions": 48210,
      "engagements": 2391,
      "likes": 1874,
      "reposts": 143,
      "replies": 262,
      "quotes": 0,
      "bookmarks": 112,
      "engagementRate": 0.0496,
      "averageImpressions": 4383,
      "topPostId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9"
    }
  ],
  "topPosts": [
    {
      "id": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9",
      "text": "Back in stock: our speckled stoneware mugs 🙌",
      "platforms": [
        "instagram",
        "threads"
      ],
      "type": "photo",
      "publishedAt": "2026-09-18T09:00:04.000Z",
      "impressions": 9120,
      "engagements": 811,
      "engagementRate": 0.0889,
      "url": "https://www.instagram.com/p/DA1b2C3d4E5/"
    }
  ],
  "notes": {
    "…": "Short caveats per platform, e.g. which platforms have no post stats."
  }
}

Note: X posts have no stats in PostNinja, so they count as published without numbers. Instagram, Threads, Facebook, TikTok, YouTube and Pinterest posts have stats.

list_comments

List comments. Comment conversations on the connected accounts' posts (Instagram, Facebook Pages, Threads, YouTube), latest first. Pass commentId to get one conversation with every message. Reply with reply_to_comment.

Read-only. Needs posts:read.

When to use it: To answer comments: list conversations with filter: needs_reply, then open one with commentId to read every message and the post it's on. Reading here doesn't mark comments as read in the app.

Parameters

  • filterstring

    needs_reply: the latest message isn't ours. One of all, unread, needs_reply, replied, hidden. Default "all".

  • platformstring

    One of instagram, facebook, threads, youtube.

  • accountIdstring (uuid)
  • commentIdstring (uuid)

    Get this conversation in full instead of the list.

  • limitinteger

    1 to 100. Default 30.

Example arguments
{
  "filter": "needs_reply",
  "platform": "instagram",
  "limit": 10
}
Example result (the text your agent receives)
Comment conversations:

{
  "threads": [
    {
      "id": "9e2a4f5b-6c7d-4e8f-a091-0c1d2e3f4a5b",
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "provider": "instagram",
      "authorName": "Jamie",
      "authorHandle": "jamie.makes",
      "authorAvatarUrl": null,
      "text": "Do these ship to Canada?",
      "postedAt": "2026-10-06T08:14:22.000Z",
      "count": 1,
      "unread": 1,
      "needsReply": true,
      "replied": false,
      "hidden": false,
      "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9"
    }
  ],
  "hasMore": false
}
Example result with commentId: one conversation, every message
Conversation:

{
  "id": "9e2a4f5b-6c7d-4e8f-a091-0c1d2e3f4a5b",
  "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
  "provider": "instagram",
  "post": {
    "text": "Back in stock: our speckled stoneware mugs 🙌",
    "thumbnail": null,
    "permalink": "https://www.instagram.com/p/DA1b2C3d4E5/",
    "postedAt": "2026-10-05T09:00:04.000Z",
    "postId": "0d3c4b5a-6978-4e1f-a2b3-c4d5e6f7a8b9"
  },
  "messages": [
    {
      "id": "9e2a4f5b-6c7d-4e8f-a091-0c1d2e3f4a5b",
      "kind": "comment",
      "fromUs": false,
      "authorName": "Jamie",
      "authorHandle": "jamie.makes",
      "authorAvatarUrl": null,
      "text": "Do these ship to Canada?",
      "postedAt": "2026-10-06T08:14:22.000Z",
      "hidden": false,
      "permalink": null,
      "sentByName": null,
      "readAt": null
    }
  ],
  "canWrite": true,
  "writeNote": null,
  "hideLabel": "Hide",
  "maxLength": 2200
}

reply_to_comment

Reply to a comment. Replies publicly to a comment as the connected account it was left on. Use a commentId from list_comments.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To reply publicly as the account the comment was left on. Use the conversation's id from list_comments. Keep replies within the platform's limit (maxLength in the conversation).

Parameters

  • commentIdstring (uuid)required
  • textstringrequired

    Up to 2,200 characters.

Example arguments
{
  "commentId": "9e2a4f5b-6c7d-4e8f-a091-0c1d2e3f4a5b",
  "text": "They do! Shipping to Canada takes 5-7 days."
}
Example result (the text your agent receives)
Reply sent.

{
  "id": "a3b4c5d6-e7f8-4a9b-8c0d-1e2f3a4b5c6d",
  "kind": "reply",
  "fromUs": true,
  "authorName": "Acme Studio",
  "authorHandle": "acme.studio",
  "authorAvatarUrl": null,
  "text": "They do! Shipping to Canada takes 5-7 days.",
  "postedAt": "2026-10-06T09:02:40.000Z",
  "hidden": false,
  "permalink": null,
  "sentByName": null,
  "readAt": "2026-10-06T09:02:40.000Z"
}

list_messages

List direct messages. Direct-message conversations on the connected Instagram and Facebook Page accounts, latest first, with each one's reply window. Pass conversationId to get one conversation with its messages. Reply with reply_to_message while window.state isn't "closed".

Read-only. Needs posts:read.

When to use it: To answer direct messages on Instagram and Facebook Pages: list conversations with filter: needs_reply, then open one with conversationId to read its messages. Check window: replies are only possible while window.state is open (24 hours after the person's last message). Reading here doesn't mark messages as read in the app.

Parameters

  • filterstring

    needs_reply: they wrote last. One of all, unread, needs_reply. Default "all".

  • platformstring

    One of instagram, facebook.

  • accountIdstring (uuid)
  • conversationIdstring (uuid)

    Get this conversation in full instead of the list.

  • limitinteger

    1 to 100. Default 30.

Example arguments
{
  "filter": "needs_reply",
  "limit": 10
}
Example result (the text your agent receives)
Message conversations:

{
  "conversations": [
    {
      "id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8",
      "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
      "provider": "instagram",
      "participantName": "Jamie Lee",
      "participantUsername": "jamie.makes",
      "participantAvatarUrl": null,
      "lastText": "Hi! Is the large mug back in stock?",
      "lastFromUs": false,
      "lastAttachments": 0,
      "lastMessageAt": "2026-10-06T08:14:22.000Z",
      "unread": 1,
      "needsReply": true,
      "window": {
        "state": "open",
        "closesAt": "2026-10-07T08:14:22.000Z"
      }
    }
  ],
  "hasMore": false
}
Example result with conversationId: one conversation with its messages
Conversation:

{
  "id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8",
  "accountId": "8b7e1d22-0c4f-4e8a-a1b2-9d8c7b6a5f40",
  "provider": "instagram",
  "participant": {
    "id": "1784001234567890",
    "name": "Jamie Lee",
    "username": "jamie.makes",
    "avatarUrl": null
  },
  "permalink": "https://ig.me/m/jamie.makes",
  "messages": [
    {
      "id": "6f7a8b9c-0d1e-4f2a-8b3c-4d5e6f7a8b9c",
      "fromUs": false,
      "text": "Hi! Is the large mug back in stock?",
      "attachments": [
        {
          "type": "image",
          "url": "https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=…",
          "previewUrl": null,
          "name": null
        }
      ],
      "sentAt": "2026-10-06T08:14:22.000Z",
      "readAt": null,
      "sentByName": null
    }
  ],
  "window": {
    "state": "open",
    "closesAt": "2026-10-07T08:14:22.000Z"
  },
  "canReply": true,
  "replyNote": null,
  "maxLength": 1000,
  "lengthUnit": "bytes"
}

Note: Attachment links point at Instagram's or Facebook's servers and stop working after a while.

reply_to_message

Reply to a direct message. Sends a private direct message in a conversation, as the connected account. Instagram and Messenger only allow it within 24 hours of the person's last message; after that it's refused. Instagram messages can be up to 1,000 bytes, Facebook up to 2,000 characters.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: To answer a direct message privately as the account it was sent to. Use the conversation's id from list_messages. Only while the reply window is open; once it has closed the tool returns an error and the person has to write again first.

Parameters

  • conversationIdstring (uuid)required
  • textstringrequired

    Up to 2,000 characters.

Example arguments
{
  "conversationId": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8",
  "text": "It is! It's back on the shop today."
}
Example result (the text your agent receives)
Message sent.

{
  "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
  "fromUs": true,
  "text": "It is! It's back on the shop today.",
  "attachments": [],
  "sentAt": "2026-10-06T09:02:40.000Z",
  "readAt": "2026-10-06T09:02:40.000Z",
  "sentByName": null
}

upload_image

Upload an image. Uploads a JPEG, PNG or WebP image (base64, up to about 3 MB) and returns a media id to use in create_post. For videos and larger files, use the REST API: POST /api/v1/media.

Changes data. Needs posts:write (or a Read & write API key).

When to use it: When the user gives the agent an image to post. Send it base64-encoded (no data: prefix), then pass the returned id in mediaIds. For videos or images over about 3 MB, upload with the REST API (POST /api/v1/media) and give the agent the id.

Parameters

  • datastringrequired

    The image file, base64-encoded (no data: prefix).

  • mimeTypestringrequired

    One of image/jpeg, image/png, image/webp.

Example arguments
{
  "data": "/9j/4AAQSkZJRgABAQAAAQABAAD…",
  "mimeType": "image/jpeg"
}
Example result (the text your agent receives)
Uploaded. Use mediaIds: ["5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"] in create_post.

{
  "id": "5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e",
  "kind": "image",
  "width": 1080,
  "height": 1350,
  "url": "https://postninja.app/media/5b9d1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e.jpg"
}