| upload_videoA | Publish a video to one or more platforms. Use videoPathOrUrl only for public/signed HTTPS URLs, or for absolute local paths when the MCP server runs on the same machine as the file. videoBase64 is only for clients that can provide raw bytes directly and is capped by UPLOAD_POST_MAX_INLINE_MB (default 100). Returns a request_id you can poll with get_status. Supports per-platform overrides (tiktokPrivacyLevel, youtubePrivacyStatus, youtubePlaylistId, youtubeThumbnailUrl, youtubeTags, facebookPageId, instagramMediaType, etc.). A hosted MCP server cannot read files on the user's machine or chat attachments (/mnt/data, sandbox paths). To publish a local file: (1) if this client can run HTTP requests itself (for example a coding agent with a shell), stage it with create_media_upload (returns upload_id + upload_url), PUT the raw file bytes to upload_url with the same Content-Type, call complete_media_upload and pass the returned media_url as the media URL; (2) otherwise ask the user for a public HTTPS URL of the file, or tell them to publish it from the dashboard at https://app.upload-post.com. Staged media is deleted after 24 hours. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| upload_photosA | Publish one or more photos (single image or carousel; items are published in array order). Each item in photosPathsOrUrls must be a public/signed HTTPS URL (a staged media_url from complete_media_upload works), or an absolute local path only when the MCP server runs on the same machine as the file. One call can carry a different caption per platform: title is the default and platformOptions.facebookTitle, platformOptions.instagramTitle, platformOptions.linkedinTitle, platformOptions.xTitle, … override it for that platform. A hosted MCP server cannot read files on the user's machine or chat attachments (/mnt/data, sandbox paths). To publish a local file: (1) if this client can run HTTP requests itself (for example a coding agent with a shell), stage it with create_media_upload (returns upload_id + upload_url), PUT the raw file bytes to upload_url with the same Content-Type, call complete_media_upload and pass the returned media_url as the media URL; (2) otherwise ask the user for a public HTTPS URL of the file, or tell them to publish it from the dashboard at https://app.upload-post.com. Staged media is deleted after 24 hours. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| upload_textA | Publish a text-only post. linkUrl (or platform-specific *LinkUrl) attaches a link preview where supported. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| upload_documentA | Publish a document (PDF / PPT / PPTX / DOC / DOCX) to LinkedIn. Title is required. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| get_statusA | Check the status of an async upload by request_id. Poll this until status is 'success' or 'failed'. |
| get_job_statusA | Check the status of a scheduled or queued upload by job_id. Use this for posts created with addToQueue or scheduledDate. |
| get_historyB | Paginated history of uploads across all profiles. |
| get_mediaA | Retrieve recent media (videos, photos, text posts) pulled directly from a profile's connected social accounts. Supports instagram, tiktok, youtube, linkedin, facebook, x, threads, pinterest, bluesky, reddit. Useful for browsing what already exists on a platform before posting more. Paginated: the response carries pagination = { limit, next_cursor, has_more }; pass next_cursor back as cursor for the next page (the last page returns next_cursor null and has_more false). |
| list_scheduledA | List all currently scheduled (not-yet-published) posts. |
| cancel_scheduledB | Cancel a scheduled post by its job_id. |
| edit_scheduledA | Edit a pending scheduled post in place, keeping its job ID and media: reschedule it (scheduledDate/timezone) and/or fix its text. title and caption rewrite the text for EVERY platform of the post; to change only some platforms, send platformContent instead, e.g. {"instagram":{"caption":"..."},"tiktok":{"caption":"..."}} — platforms not listed keep their text. youtubePublishAt sets or clears the date YouTube itself makes the video public (native YouTube scheduling). First comments cannot be edited on a pending post. |
| get_analyticsA | Aggregated analytics for a profile across selected platforms (followers, views, engagement). Instagram also returns two audience breakdowns with the same shape (age / gender / country / city): follower_demographics for the account's followers and engaged_audience_demographics for the accounts that engaged with its content. |
| get_total_impressionsB | Sum of impressions for a profile from daily snapshots. Use period for presets, or startDate/endDate for custom ranges. |
| get_post_analyticsA | Per-platform metrics for a specific post identified by request_id. post_metrics carries whatever each platform reports, so its shape is not the same everywhere: on TikTok it adds retention (the curve, second by second), impression_sources (For You, following, search, profile…), audience_types (followers vs non-followers), new_followers won by the post, reach and the watch times (average_time_watched, total_time_watched, full_video_watched_rate) on top of the usual counters. It fetches the metrics live from the platform and changes nothing on the post or the account; Upload-Post keeps a copy of the result, which later reads of the same post can get from get_cached_post_analytics. Repeated calls for the same post within 5 minutes return the same result. When one platform cannot answer (for example, X's daily limit on live post reads), its entry carries post_metrics_error instead of post_metrics and the other platforms still answer. |
| get_cached_post_analyticsA | Replays per-post metrics Upload-Post already fetched, instead of calling the platforms again. ONLY contains posts previously fetched through get_post_analytics; there is no background refresh, so captured_at is the last time that post was read live and a post never queried live will be absent. Unlike get_post_analytics it never hits the platforms, so it is not subject to the live analytics rate limit (100 requests / 5 minutes) — prefer it when scanning many posts or paging through a profile's history. Paginated: pass next_cursor from the response back as cursor until has_more is false. |
| get_platform_metricsA | Reference: which metrics are available per platform (impressions, likes, …) and their human labels. |
| get_audienceA | Who follows the profile, when they are online and what they tap on it. One endpoint for every network, chosen with platform. Returns audience.countries / .cities / .ages / .genders, activity_by_hour (followers online per hour of the day — use it to pick posting times), followers_daily (total / new / lost), profile_actions and bio_description. It always returns benchmark_categories (the 25 values benchmark_category accepts), and when benchmark_category is set it adds a benchmark object with that niche's averages (engagement rate, likes, views, follower growth…) to compare the account against. The window is clamped by the server — at most 60 days, always ending before today — so a wider range is trimmed, not rejected; range in the response says which window was actually used. Requires the 'profile_analytics' capability on the profile's TikTok account (see the capabilities array in list_users). |
| get_suggestionsA | What to write about: the hashtags or the searches a network suggests around a word. One endpoint for both questions, told apart by type, and one endpoint for every network, chosen with platform. type: 'hashtags' returns { hashtags: [{ name, view_count }] } — pick tags that actually have reach for a caption; type: 'keywords' returns { keywords: [...] }, what people really search around q, useful for the next video topic. Requires the 'profile_analytics' capability on the profile's TikTok account (see the capabilities array in list_users). Keyword suggestions need 'trend_search' instead, granted the same way. |
| get_account_infoA | Return the connected Upload-Post account: email, plan and usage limits. Useful as a first call to confirm the connection works before doing real work. |
| list_usersA | List all Upload-Post profiles in the account, with their connected social accounts. Call this FIRST, before publishing, to get the exact profile name (user) and check that the target platforms are connected; never invent a profile name. When the account has no profile, or the requested platform is not connected or needs reconnecting, the response carries next_step and connect_url: give the user that link, ask them to tell you when they have connected, and wait — do not try to publish meanwhile. The TikTok account object carries a capabilities array (music, location, cover_image, cover_timestamp, draft, photo_privacy, video_privacy, inbox_fallback, profile_analytics) telling which optional TikTok fields that connection accepts. |
| get_connect_linkA | Get the link the user opens to connect (or reconnect) social accounts such as TikTok, Instagram or YouTube. Use it when list_users shows no profile, or the platform the user wants is not connected or needs reconnecting. If the account has no profile yet, this creates one named "default" and returns a one-click link to connect accounts to it (valid 48 h); otherwise it returns the dashboard link. Give the user connect_url, ask them to tell you when they are done, then call list_users to confirm before publishing. |
| create_userB | Create a new Upload-Post profile (logical container for connected socials). |
| delete_userA | Permanently delete a profile and disconnect its socials. Needs the user's approval: the call only deletes when confirm_username repeats the profile name, typed or approved by the user. Without it the response says what would be deleted. |
| generate_jwtA | Generate a JWT + connection URL so an end-user can connect socials inside an embedded Upload-Post flow (white-label integration). The profile must already exist. Each call resets the profile's connection-page settings to the values passed. To simply help the account owner connect their own socials, prefer get_connect_link. |
| validate_jwtA | Verify a JWT previously issued by generate_jwt. The returned profile object echoes the connection page settings, including language and any ui_labels. |
| get_facebook_pagesC | Facebook pages connected to a profile. |
| get_linkedin_pagesA | LinkedIn company pages connected to a profile. |
| get_pinterest_boardsC | Pinterest boards available to a profile. |
| tiktok_music_trendingA | Trending tracks from the TikTok Commercial Music Library, to soundtrack a TikTok video. Pass the returned track id as tiktokMusicId in upload_video's platformOptions (not commercial_music_id, which TikTok rejects on public posts). Available on TikTok connections that declare the music capability (see capabilities on the TikTok account in list_users). |
| tiktok_music_searchA | Find a TikTok Commercial Music Library track by song title or artist, to soundtrack a TikTok video. Pass the returned track id as tiktokMusicId in upload_video's platformOptions (not commercial_music_id, which TikTok rejects on public posts). IMPORTANT: TikTok has no music search endpoint, so this searches the trending charts Upload-Post caches per genre/country/period, NOT TikTok's whole catalogue — a song that is not trending in the chart you query will not be found; widening the search means trying another genre, country or period. Matching is case- and accent-insensitive and every word must match. Available on TikTok connections that declare the music capability (see capabilities on the TikTok account in list_users). |
| tiktok_location_searchA | Search TikTok places to tag on a post. TikTok needs both parts, so pass the returned location_id as tiktokLocationId and location_name as tiktokLocationName in upload_video's platformOptions. Available on TikTok connections that declare the location capability (see capabilities on the TikTok account in list_users). |
| tiktok_publishing_settingsA | What the connected TikTok account is allowed to publish. Call this before setting tiktokPrivacyLevel: TikTok narrows the four privacy values per account (a private account has no PUBLIC_TO_EVERYONE), and sending one the account does not have fails the upload with error_code tiktok_privacy_unavailable. Returns privacy_level_options plus the account's max video duration and its comment/duet/stitch switches. |
| get_google_business_locationsB | Google Business Profile locations the profile can post to. |
| get_google_business_reviewsA | List reviews for a Google Business Profile location. Pass the profile that owns the connected Google Business account; location_id defaults to the account's selected location. |
| reply_to_google_business_reviewA | Create or update the owner reply to a Google Business review. Provide review_name (the full resource path from get_google_business_reviews) or review_id + location_id. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| get_reddit_detailed_postsA | Recent Reddit posts published from a profile, with the platform-side metadata (subreddit, flair, score, …). Reddit posting is currently unavailable (error_code=reddit_unavailable) until the Reddit app is restored. Do not use reddit as a publish target. |
| get_post_commentsA | List comments on a post. One endpoint for every network, chosen with platform. Identify the post by either postId or postUrl (YouTube: postId=videoId; LinkedIn: postId=the post urn; TikTok: postId=the video id, postUrl is not accepted). Add commentId to get the REPLIES hanging off that one comment instead of the post's top-level comments — replies are the same question with one more parameter, not another tool. Reads are cached for up to 60 seconds (up to 2 minutes on X); a comment created or deleted through Upload-Post by the same account shows up on the next read. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. |
| reply_to_commentA | Send a private DM to the author of an Instagram comment (within Instagram's 7-day reply window). Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| public_reply_to_commentA | Post a public reply visible under the original Instagram comment. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| create_commentA | Post a top-level comment or a reply on a post. Provide exactly ONE of commentId (reply to a comment), postId, or postUrl (top-level). LinkedIn: postId=the post urn. Instagram requires commentId (replies only). TikTok always needs postId (the video id); add commentId on top of it to reply inside that thread. X: links are removed from the message unless the account is allowed links on X (the response then lists it in warnings), and a message that was only links is refused with 400 x_links_not_allowed. Replies on X share a daily limit per X account with post edits and reposts made through Upload-Post (20 a day on the free plan, 200 on paid plans, reset at 00:00 UTC); past it the call returns 429 x_daily_write_limit with retry_after in seconds. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| delete_commentA | Delete a comment by commentId. LinkedIn also requires postId (the post urn); TikTok needs only the commentId. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. |
| comment_actionA | Moderate or react to a comment on one of the profile's own posts. One endpoint for every network, chosen with platform; each network supports its own verbs and answers 400 platform_not_supported / invalid action otherwise. TikTok: hide/unhide (postId), like/unlike (no postId), pin/unpin (postId). Facebook: hide/unhide, like/unlike, edit (needs message). Instagram: hide/unhide a comment, or enable_comments/disable_comments on a post (postId, no commentId). YouTube: hide/unhide/hold (postId; banAuthor only with hide). Threads: hide/unhide, approve/ignore. Most actions have an inverse; edit does not keep the previous text and banAuthor bans the commenter from the YouTube channel, so both need the user's approval of the exact change. Deleting a comment is a different tool: delete_comment. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| hide_commentA | Hide a comment on one of the profile's own posts so the public no longer sees it, or show it again with hidden: false. Works on TikTok, Facebook, Instagram, YouTube and Threads. TikTok and YouTube need the postId the comment belongs to. To delete a comment use delete_comment. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| like_commentA | Like a comment as the profile's account, or remove that like with liked: false. Works on TikTok and Facebook. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. |
| pin_commentA | Pin a comment to the top of one of the profile's own TikTok videos, or unpin it with pinned: false. TikTok only. TikTok: Requires the 'comments' capability on the profile's TikTok account (see the capabilities array in list_users). It is granted at connection time, so an account connected earlier has to reconnect TikTok before this works. |
| edit_commentA | Replace the text of a comment the Page itself wrote on Facebook. Facebook only. The previous text is not kept. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| hold_commentA | Move a comment on one of the channel's YouTube videos to "Held for review", taking it off the public video until it is approved. YouTube only. To publish it again use hide_comment with hidden: false. |
| approve_commentA | Approve a reply waiting for approval on one of the profile's Threads posts, so it shows publicly. Threads only; to leave it unpublished use ignore_comment. |
| ignore_commentA | Ignore a reply waiting for approval on one of the profile's Threads posts, so it stays unpublished. Threads only; to publish it use approve_comment. |
| set_post_commentsA | Turn commenting off on one of the profile's own Instagram posts, or back on with enabled: true. Existing comments are kept either way. Instagram only. |
| retry_postB | Retry a failed upload. Identify it by either requestId (async upload) or jobId (scheduled/queued upload). |
| unpublish_postA | Delete a post already published to a platform. Supported: facebook, youtube, x, linkedin, pinterest, bluesky, google_business. Instagram, TikTok, Threads and Reddit posts cannot be deleted through the API; they have to be removed in the platform's own app. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| send_dmA | Send an Instagram DM to a recipient from a connected Upload-Post profile. Use a recipient_id returned by Instagram comments or DM conversation tools. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| list_dm_conversationsC | Recent DM conversations for a profile on a given platform. |
| manage_autodmsA | Control AutoDM monitors, which answer every comment on a post (or only those containing trigger_keywords). Instagram answers with a private DM; TikTok, YouTube, Facebook, Threads and Bluesky answer with a public reply under the comment. For action='start', provide profile_username, reply_message and the post (post_url on Instagram; post_id or post_url elsewhere, plus platform). For stop/pause/resume/delete/logs, provide monitor_id. For status, optionally set include_inactive=true. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| start_autodmA | Watch one of the profile's posts and automatically answer everyone who comments on it (optionally only comments containing trigger_keywords, e.g. 'comment POST'). On Instagram (default) the answer is a private DM with reply_message and up to 3 link buttons. On tiktok, youtube, facebook, threads and bluesky it is a PUBLIC reply under each matching comment; links in TikTok replies are not clickable, so point people to the bio. Instagram and TikTok answer new comments within seconds; youtube, facebook, threads and bluesky check for them every monitoring_interval minutes (15 minimum). At most 25 replies per interval (10 on YouTube), and the monitor stops after 15 days. Returns the monitor ID used by the other AutoDM tools. Needs the user's approval: call it first without confirmed to get a preview (nothing is sent), show the user exactly what will go out, and repeat the call with confirmed: true only after they approve. |
| list_autodmsA | List the account's AutoDM and comment auto-reply monitors with their platform, state and counters. Active ones only, unless include_inactive is true. |
| get_autodm_logsA | Read the activity log of one AutoDM monitor: comments seen and DMs sent or failed. |
| pause_autodmA | Pause an AutoDM monitor: no DMs are sent until resume_autodm. |
| resume_autodmA | Resume a paused AutoDM monitor, so it sends DMs to new matching comments again. |
| stop_autodmA | Stop an AutoDM monitor: it stops sending DMs and shows as stopped in list_autodms (with include_inactive). For a temporary break use pause_autodm; to remove it entirely use delete_autodm. |
| delete_autodmA | Permanently delete an AutoDM monitor with its stats and logs. Cannot be undone. |
| submit_ffmpeg_jobA | Process media with FFmpeg on Upload-Post's encoding servers (trim, transcode, resize, watermark, concatenate, extract a thumbnail…). Inputs are public HTTP(S) URLs: input_url for one file, or files for several. They are downloaded into a working directory of their own for this job. full_command is one ffmpeg invocation using the placeholders {input} (or {input0}, {input1}… with several files) and {output}, e.g. ffmpeg -i {input} -ss 5 -t 10 -c copy {output}. It runs without a shell and must start with ffmpeg. Every -i must be an {input}/{inputN} placeholder or a generated -f lavfi source, and {output} is the only output. Rejected with a 400 that names the problem: shell metacharacters (; | & $ ), protocols or URLs anywhere in the command (file:, concat:, subfile,…), the movie=, amovie=andsendcmdfilters, absolute paths other than fonts under /usr/share/fonts,.., and -safe 0. A command that would never end (-stream_loop -1, -loop 1, or a lavfi source without a duration) needs -t, -shortestor-frames; -stream_loopaccepts -1 to 100. Usage is charged at the longer of the input and output durations, so looped or generated outputs count at their full length. The output is not posted anywhere; fetch it with download_ffmpeg_result. Returns ajob_idyou can poll withget_ffmpeg_job`. |
| get_ffmpeg_jobA | Poll the status of an FFmpeg job. When status is 'finished', call download_ffmpeg_result to obtain a publishable media_url (or the authenticated download URL). |
| download_ffmpeg_resultA | Returns how to get a finished FFmpeg job's output without streaming it through MCP. For video and image outputs it returns a temporary public media_url (signed, no API key, deleted 24 h after the job finished) that you can pass straight to upload_video / upload_photos as the video URL / photo URL. It also returns download_url, which needs the user's Upload-Post API key. |
| get_ffmpeg_consumptionA | Monthly FFmpeg processing minutes used vs. plan allowance. |
| get_queue_settingsC | Posting queue configuration (slots per day, time windows, timezone). |
| update_queue_settingsA | Update posting queue configuration for a profile. Fields are flat, not nested: timezone, slots, days_of_week, and max_posts_per_slot. |
| preview_queueA | Preview the upcoming queue slots and what would land in them. Optionally nextSlot=true returns just the next available slot timestamp. |
| create_media_uploadA | Step 1 of staging a local file for publishing. Returns upload_id and a short-lived upload_url; the caller must then PUT the raw file bytes to upload_url (same Content-Type and size as declared here) and call complete_media_upload. Only useful when this client can perform the HTTP PUT itself (for example a coding agent with a shell); the model cannot read chat attachments or /mnt/data paths, so for those ask the user for a public HTTPS URL or send them to https://app.upload-post.com. Staging media is deleted after 24 hours; scheduled posts are safe because upload_video copies the media into durable scheduler storage. |
| complete_media_uploadA | Step 2 of staging a local file. Call it only after the file bytes were actually PUT to the upload_url from create_media_upload; without that PUT it fails or yields no publishable media. Returns a temporary media_url to pass straight to upload_video / upload_photos / upload_document. |
| get_media_uploadA | Internal/app staging helper. Get status for a short-lived MCP media upload. Optionally returns a fresh temporary media_url for an already-uploaded staging object. |