Every change Wellknown observed on this MCP server, newest first, with what it was before and what it became. Tool-surface changes carry the definition diff. Nothing here is edited after the fact.
Authorization required, Bearer, issuer https://api.uplika.com/api/auth
Authorization required, Bearer, issuer https://api.uplika.com/api/auth
Authorization required, Bearer, issuer https://api.uplika.com/api/auth
Authorization required, Bearer, issuer https://api.uplika.com/api/auth
Authorization not required → required, Bearer, issuer https://api.uplika.com/api/auth
Changed the definition of "publish_naver_draft"
⟨126 unchanged words⟩ Naver, use update_post on that post instead. A draft left by a failed publish (the failure text says "A half-finished Naver draft may have been left") is not the finished post: do not publish it with this tool; send the original post again (retry_post) and delete that draft in Naver. If the response carries `bridge`, `bridge.nextStep` says what ⟨53 unchanged words⟩
Changed the definition of "delete_automation", "disable_automation", "duplicate_automation" and 9 more
⟨11 unchanged words⟩ ,"type":"boolean"},"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"workspaceId":{"description" ⟨44 unchanged words⟩
{"properties":{"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"workspaceId":{"description" ⟨44 unchanged words⟩
Changed the definition of "bridge_status", "check_naver_visibility", "delete_post" and 14 more
Check the Naver Blog extension and how to wakeChromeits browser
⟨10 unchanged words⟩ the uplika browser extension inside the user's own browser (Chrome, Edge or Whale), so nothing goes out while thatChromebrowser is closed. Call this before publishing to Naver ⟨6 unchanged words⟩ response carries wake commands per OS that openChromethat browser (browser says which) on the user's computer in the profile that ⟨6 unchanged words⟩
Changed the definition of "check_naver_visibility", "diagnose_naver_blog", "expand_naver_keywords" and 6 more
⟨209 unchanged words⟩ reports what the page shows at that moment.Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨131 unchanged words⟩ per day (quota in the response).Returns 403 research_tool_disabledThiswithreadsenableUrl
Changed the definition of "list_naver_keyword_reports" and "research_naver_keywords"
⟨31 unchanged words⟩ belong to the person, not a workspace.The answer also carries citationRules (Uplika's measured sentence rules); prompts saved before 2026-10-12 do not include that section themselves.Returns 403 research_tool_disabled with enableUrl if the person ⟨23 unchanged words⟩
⟨163 unchanged words⟩ topic to put the post's subject in it.The answer also carries citationRules: sentence rules Uplika measured on posts Naver's AI briefing quoted. They come even when no set forms (every keyword a wall or phantom), so a post written without a set can still follow them.Returns 403 research_tool_disabled with enableUrl if the person
Added "check_naver_visibility", "get_naver_visibility" and "scan_naver_blog_visibility"; changed the definition of "list_naver_keyword_reports", "publish", "research_naver_keywords" and 1 more (62 tools before, 65 now)
⟨31 unchanged words⟩ belong to the person, not a workspace. The answer also carries citationRules (Uplika's measured sentence rules); prompts saved before 2026-10-12 do not include that section themselves. Returns 403 research_tool_disabled with enableUrl if the person ⟨23 unchanged words⟩
⟨1801 unchanged words⟩ "description":"Who can see it. public (the default for a new post), neighbor, mutual (mutual neighbors only), or private.
Added "search_instagram_audio"; changed the definition of "publish" (61 tools before, 62 now)
⟨626 unchanged words⟩ it as a story if the edges matter. A reel takes music, or another reel's sound, through options.instagram.audio with an id from search_instagram_audio (Instagram accounts connected via Facebook only); for a voice-over set volume around 20 and keep videoVolume at 100, and pick audio at least as long as the reel because it plays once from its start. Facebook publishes to a Page, never a personal ⟨491 unchanged words⟩
⟨719 unchanged words⟩ media works without it.","properties":{"audio":{"description":"Music or a sound from search_instagram_audio to put on a reel. Reels only, and only on Instagram accounts connected via Facebook. Instagram defaults both volumes to 100, which can drown a voice-over; for narration try volume 20 and videoVolume 100. Instagram cannot preview the mix before it is published. The audio plays once from its beginning (you cannot choose the start) and is not repeated, so pick one at least as long as the reel.","properties":{"id":{"description":"The audio id from search_instagram_audio. Digits only. Use an id from this account's own results.","type":"string"},"videoVolume":{"description":"Loudness of the video's own sound, a whole number from 0 to 100. Default 100.","type":"number"},"volume":{"description":"Loudness of the added audio, a whole number from 0 to 100. Default 100.","type":"number"}},"required":["id"],"type":"object"},"
Changed the definition of "refresh_account"
⟨56 unchanged words⟩ the refresh runs when that browser comes back.OtherOnchannelsThreads,returnInstagramrefresh_unsupportedandbecauseFacebooktheirthismetadatare-reads the display name and the profile picture. It does not renew a sign-in: a connection whose sign-in the channel has ended answers account_expired, and that account islivereconnected
Changed the definition of "search_youtube_videos"
⟨6 unchanged words⟩ returns the top videos with views, subscribers, duration and publish date as YouTube gives them, plus two values uplika calculates from them that are not YouTube metrics (dataNotes says so): the views-to-subscribers ratio (above 1 means the title and topic pulled more people than the channel has),and Shorts or long-form (60 seconds or less counts as a Short), duration and publish date.. order is viewCount, date or relevance; period ⟨119 unchanged words⟩
Changed the definition of "list_channel_posts" and "publish"
⟨56 unchanged words⟩ TikTok this reads public videos only, soitvideosstayssetemptytowhileOnlytheme,appvideosisstillawaitingwaitingTikTok'sinContent
Changed the definition of "search_youtube_videos"
⟨101 unchanged words⟩ today's are gone). Carries prompt, a brief in the person's Uplika language (Koreanbriefor English) that turns the table into title and hook ⟨46 unchanged words⟩
Changed the definition of "like"
⟨127 unchanged words⟩ back as naver_not_allowed rather than a fake success. On Naver Blog a secret comment, a comment hidden by Cleanbot or by the blog's blocked keywords, and every comment on a blog that turned comment likes off have no like button; those answer naver_not_allowed too, and retrying does not help. If the response carries `bridge`, `bridge.nextStep` says what ⟨45 unchanged words⟩
Changed the definition of "delete_post", "get_post", "publish" and 1 more
⟨206 unchanged words⟩ app cannot be deleted through us. If the account that published the post was disconnected, this returns 409 account_disconnected and deletes nothing on any channel; reconnect the same account and the post id comes back. If the response carries `bridge`, `bridge.nextStep` says what is needed ⟨43 unchanged words⟩
⟨11 unchanged words⟩ a post link you probably want open_post instead. A target whose account was disconnected keeps its status and link, with connectionId null and accountHandle set to that account's handle; editing, deleting or retrying the post then returns 409 account_disconnected until the same account is reconnected, and the same post id comes back with it. If the response carries `bridge`, `bridge.nextStep` says what
Changed the definition of "get_publish_options", "put_automation" and "validate_automation"
CheckTikTokwhatonly:acreatorchannelsettingsneedsto check before publishing
What a channel needs to know beforeTikTokyouonly.publish
Changed the definition of "publish"
⟨273 unchanged words⟩ returns 422 extension_outdated with the installed and requiredversions;versions.askUplikatheaskspersonthat Chrome to update at theextension.same moment, so follow the message: usually call again in a minute or two. Text limits differ per channel: Threads 500 characters ⟨244 unchanged words⟩ caption. Non-JPEG images are converted for Instagram automatically. Instagram feed images (single or carousel) must be between 4:5 and 1.91:1: one outside that range is not refused, it is center-cropped to the nearest of the two on a copy, and that target's warning says so. A 3:4 phone photo loses about 6% of its height; a 9:16 image loses 30%, so crop it yourself or send it as a story if the edges matter.
Added "send_to_past_comments"; changed the definition of "get_automation" (60 tools before, 61 now)
⟨18 unchanged words⟩ when the flow still has its template shape. pastComments is the latest send_to_past_comments job with its progress, or null. Pass version to read an older saved version's document instead (list_automation_versions).
Send an automation to past comments — Run a live comment automation on comments that are **already on the post**: the ones it missed because they came before it was enabled, during an outage, or while another tool handled the account. An automation normally reacts only to new comments. How Meta works: a comment can get one private reply, and only within 7 days. Older comments cannot be reached, and a comment another app already answered by DM is refused by Meta (counted as alreadyReplied, not as a failure). Instagram and Facebook only; top-level comments only. mode preview sends nothing: it reads the newest comments and answers e…
Changed the definition of "create_comment_to_dm", "enable_automation", "list_automation_templates" and 3 more
⟨307 unchanged words⟩ files are rejected because the window never opens. The optional public reply under the comment is fixed sentences (publicReply) or written by the AI for each comment (publicReplyMode: "ai" with publicReplyInstruction); either way it is posted only after the private reply went out, and the AI is told a DM was sent.
⟨231 unchanged words⟩ :"string"},"type":"array"},"likeComment":{"description":"Like the comment first. Instagram accounts connected through Facebook only.","type":"boolean"},"match":{"enum":["contains","is" ⟨110 unchanged words⟩ {"type":"string"},"type":"array"},"publicReplyInstruction":{"description":"With publicReplyMode ai: what the public reply should say. DM contents (links, codes, prices) are never repeated in public.","type":"string"},"publicReplyMode":{"description":"fixed (default) uses publicReply. ai: the AI writes the public reply for each comment in the channel's persona; needs publicReplyInstruction. Counts toward the daily AI limit.","enum":["fixed","ai"],"type":"string
Changed the definition of "bridge_status", "list_automation_runs", "publish" and 2 more
⟨112 unchanged words⟩ is on but not logged in to Naver), login_needed (the Naver login saved for that blog in that Chrome is signed out; its posts wait until the person logs in again from the dashboard). userMessage is a sentence in the person's ⟨35 unchanged words⟩
Runs of one automation with their status and a step log. Statusnamessayswhywhere a run is or why it stopped:
Changed the definition of "create_comment_to_dm"
⟨311 unchanged words⟩ {"description":"Optional public replies under thecomment;comment, oneispicked at random. Posted after the private reply goes out, and skipped when it could not be sent, so a reply saying a DM was sent stays true.","items":{"type":"string"}, ⟨71 unchanged words⟩
Changed the definition of "put_automation" and "update_automation"
Edit an automation:changeby replacing itstriggers,wholemessagesdocument: for flows no template covers, orstepsthatbywerereplacingalreadyitseditedwholeon
Added "delete_automation", "duplicate_automation", "list_automation_versions" and 2 more; changed the definition of "create_comment_to_dm", "get_automation", "list_automations" and 2 more (55 tools before, 60 now)
⟨172 unchanged words⟩ media_complete with one) and pass deliver.mediaId (photosand, videos, PDF or HWP/HWPX documents; on Instagram a document other than PDF goes as a download link, because Instagram DMs attach PDF only). Not both. post can be our ⟨106 unchanged words⟩
⟨6 unchanged words⟩ nodes, start. Also returns version, which put_automationneedsand update_automation need
Added "get_help"; changed the definition of "create_comment_to_dm" (54 tools before, 55 now)
⟨83 unchanged words⟩ no DMs at all; use create_automation with comment_public_reply.FilesAgofilethroughtomedia_presigndeliver: if it already has a public https link that returns the file itself (jpeg, png, gif, webp, mp4, mov or PDF), pass it as deliver.fileUrl and it goes to the person as-is; the link is opened once on save and a web page (a Google Drive or Dropbox share page), a private address or a dead link is rejected. If the file has no public link, upload it to Uplika first (media_upload_link without a shell, media_presign then media_complete with one) andarepasspasseddeliver.mediaId
Changed the definition of "enable_automation", "like" and "list_mentions"
⟨43 unchanged words⟩ a post. Refuses with reconsent_required if the accountwasdoesconnectednotbeforehold the DM permissions (the person reconnects it), with feature_in_review if the flow uses a feature whose permission is still in Meta app review (a mention trigger, liking a comment on Instagram) and the account does not already hold it, and warns if the account is not subscribed to webhooks.
Changed the definition of "like", "publish" and "put_automation"
⟨44 unchanged words⟩ Naver Blog and on Instagram accounts connected throughFacebook;Facebook that hold the like permission; any otherchannelsInstagram account and every other channel answer not_supported. On Naver Blog the post can ⟨84 unchanged words⟩
⟨2029 unchanged words⟩ API for deleting a post or reading comments.While this appUntil
Changed the definition of "publish"
⟨1370 unchanged words⟩ ,"photo-story"],"type":"string"},"imageSize":{"description":"Size of every single photo that has none of its own: fit is the document width, small is the editor's Small (about three quarters). A photo line's own {.fit} / {.small} wins. Leave it out for the default: document width for photos wider than 693px, original width for narrower ones. @group photos are always document width. Needs uplika extension 0.7.13 or later; with an older one the post still goes out and, if fit was asked for, a warning says so.","enum":["fit","small"],"type":"string"},"layout":{"description":"template (default) reshapes ⟨1616 unchanged words⟩
Changed the definition of "expand_naver_keywords"
⟨69 unchanged words⟩ each seed. Up to 10 seeds. Cached sevendays;days.aTherecalliswithnoatdailyleastcap:onetheuncachedextensionseedpathspends
Changed the definition of "retry_post" and "update_post"
⟨168 unchanged words⟩ only if the draft is not there. If another post with the same content and files is queued, going out, published or scheduled on the same account, this returns 409 duplicate_post with that post's postId and retries nothing; force: true sends it anyway. If nothing failed you get nothing_to_retry. Only applies to ⟨57 unchanged words⟩
⟨3 unchanged words⟩ :"Retry even though a Naver target isnaver_draft_unknown.naver_draft_unknownPass(pass it only after list_naver_drafts shows the draft is notthere.there), or even though another post with the same content is on the same account (duplicate_post).
Changed the definition of "publish" and "publish_naver_draft"
Only numbers and dates differ: this server embeds live figures in the definition, so the text moves without the contract changing.
⟨2912 unchanged words⟩ 10 seconds, posts with media up to9075 seconds. If it is still going after that ⟨66 unchanged words⟩
⟨87 unchanged words⟩ :{"description":"Hold until the extension finishes,(up to575
Changed the definition of "update_post"
⟨673 unchanged words⟩ exactly as it is in Naver, call publish_naver_draft. Editing a post that is already live is asynchronous: pass wait: true to hold the response for the result (up to 75 seconds), or read it with get_post.
⟨245 unchanged words⟩ :"object"},"type":"array"},"wait":{"description":"Only when editing a post that is already live: hold the response until the edit has gone through or not, up to 75 seconds. Past that it returns while still working, with next telling you to read get_post.","type":"boolean"},"workspaceId":{"description":"Which workspace this is ⟨40 unchanged words⟩
Changed the definition of "bridge_status", "create_comment_to_dm", "delete_post" and 13 more
⟨57 unchanged words⟩ has the extension (found by extension id). If you have a shell on that computer,run the oneforitstheOS,personthentocallrun.thisOnceagainChromeorisget_post;open
Changed the definition of "diagnose_naver_blog", "expand_naver_keywords", "get_naver_keyword_history" and 5 more
⟨145 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
Changed the definition of "publish" and "update_post"
⟨1042 unchanged words⟩ ":{"description":"Center every paragraph andimage.image that has no alignment of its own. A photo line's own {.left} / {.center} / {.right} wins.","type":"boolean"},"category":{ ⟨1926 unchanged words⟩
⟨50 unchanged words⟩ value; options you pass replace the whole optionsobject.object (on a post written directly on the channel they are merged per channel key instead). scheduledAt moves the send time (same rules as ⟨105 unchanged words⟩
Changed the definition of "media_from_url" and "update_post"
⟨59 unchanged words⟩ file URL that ends in the file itself. Accepted formats (read from the bytes, not the headers): image/jpeg, image/png, image/gif, image/webp, video/mp4, video/quicktime. Each channel then checks what it takes when you publish. If you can see the image, write altText describing it.
⟨151 unchanged words⟩ channels that cannot edit a live post. A post written directly on the channel (one a link or id resolved to, origin imported) can be edited on YouTube and on Facebook text or link posts: uplika reads the post from the channel first, so fields you leave out keep the channel's current values. Elsewhere it returns post_not_editable with the reason (list_platforms feature update_imported). A published Naver post is rewritten in place (same
Changed the definition of "expand_naver_keywords"
Naver Blog only. Expands seed keywords one level through Naver autocompleteon the serverand returns every word found with the seed ⟨14 unchanged words⟩ to measure with research_naver_keywords, not proven keywords. The person's uplika Chrome extension looks them up in the background when it is on (no window opens); otherwise our server does, and via says which path answered each seed. Up to 10 seeds. Cached seven days; a ⟨8 unchanged words⟩ one of 20 expansions per person per day.Server-side autocomplete isWhenoffthebyanswerdefault:iswhennaver_autocomplete_unavailable,the
Changed the definition of "publish"
⟨801 unchanged words⟩ person which timezone they mean instead of guessing. options.naver_blog.scheduledAt means the same as scheduledAt on the post (we hold the post and send it then; Naver's own reservation is not used), so pass the post-level one. A scheduled or draft post comes back with ⟨160 unchanged words⟩
⟨1459 unchanged words⟩ :"string"},"scheduledAt":{"description":"ScheduleSametheaspostscheduledAtinsideonNaver
Changed the definition of "research_naver_keywords"
⟨147 unchanged words⟩ whole post (title, subheadings, body, and[사진: …]bracketed photoslotsplaceholders for the person to fill); pass topic ⟨29 unchanged words⟩
Recently observed → Live
Changed the definition of "publish" and "research_naver_keywords"
⟨1124 unchanged words⟩ Naver files the post under the wrong board. Not needed and ignored when draftOnly is true (a draft has no board until it is published).","type":"string"},"content":{ ⟨68 unchanged words⟩ , or you publish it later with publish_naver_draft. categoryId, tags and openType are not stored on a draft; pass them when you publish it. Different from draft on the post, which keeps ⟨190 unchanged words⟩ , mutual (mutual neighbors only), or private. Ignored when draftOnly is true.","enum":["public","neighbor"," ⟨141 unchanged words⟩ tags without the # sign and without spaces. Ignored when draftOnly is true.","items":{"description":"One tag ⟨1340 unchanged words⟩
Live → Recently observed
Changed the definition of "approve_reply", "bridge_status", "create_automation" and 51 more
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Approve a held reply (Threads)"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Check the Naver ⟨7 unchanged words⟩
Recently observed → Live
Added "diagnose_naver_blog", "expand_naver_keywords", "get_naver_keyword_history" and 3 more; changed the definition of "delete_post", "follow", "get_insights" and 12 more (48 tools before, 54 now)
⟨204 unchanged words⟩ the Facebook app cannot be deleted through us. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨52 unchanged words⟩ comes back as already. There is no unfollow. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
Live → Recently observed
Added "update_media"; changed the definition of "media_from_url", "media_presign" and "publish" (47 tools before, 48 now)
{"properties":{"aiGenerated":{"description":"true when the image or video was generated or substantially changed with AI. Naver Blog photos get its AI usage label (AI 활용 설정); Instagram, YouTube and TikTok get their AI declaration turned on unless you set it to false. Leave it out when unsure.","type":"boolean"},"altText":{"description":"What the image shows ⟨71 unchanged words⟩
{"properties":{"aiGenerated":{"description":"true when the image or video was generated or substantially changed with AI. Naver Blog photos get its AI usage label (AI 활용 설정); Instagram, YouTube and TikTok get their AI declaration turned on unless you set it to false. Leave it out when unsure.","type":"boolean"},"altText":{"description":"What the image shows ⟨115 unchanged words⟩
Added "list_naver_drafts" and "publish_naver_draft"; changed the definition of "delete_post", "like", "publish" and 2 more (45 tools before, 47 now)
⟨101 unchanged words⟩ this simply cancels it and removes our record. A Naver draft (target externalId starting with draft:) is only in Naver's draft box: this cancels our record and, with extension 0.7.2 or later, removes that draft too. On Threads and YouTube this also works on ⟨67 unchanged words⟩
Like a post,— or a comment: pass replyTo (a replyunderid
Changed the definition of "create_comment_to_dm", "enable_automation" and "put_automation"
⟨135 unchanged words⟩ publish to do both in one call). Only one enabled automation per account can wait for "next" (409 next_post_taken). Order when everything is on: opening DM ( ⟨48 unchanged words⟩
⟨11 unchanged words⟩ to real people.** Confirm with the person first. Only one automation per account can wait for the next post: enabling a second one is refused with next_post_taken until the first binds to a post. Refuses with reconsent_required if the account was connected ⟨13 unchanged words⟩
Added "describe_grammar"; changed the definition of "bridge_status", "create_comment_to_dm", "publish" and 2 more (44 tools before, 45 now)
⟨101 unchanged words⟩ same bridge object when the extension is offline. state is one of online, offline, logged_out (Chrome is on but not logged in to Naver). userMessage is a sentence in the person's language to relay as it is. queued is how many posts wait for the extension; delete_post cancels them and update_post rewrites them before they go out. extensionVersion and kinds say what that extension can do.
⟨135 unchanged words⟩ publish to do both in one call). Order when everything is on: opening DM (message + button) -> askEmail -> requireFollow -> deliver (text, up to three link buttons, file; clicks are tracked) -> followUp if no link was clicked. openingDm:false sends deliver as the private reply itself; then requireFollow, askEmail, followUp and files are rejected because the window never opens.
Added "approve_reply", "create_automation", "create_comment_to_dm" and 12 more; changed the definition of "list_platforms" and "publish" (29 tools before, 44 now)
⟨23 unchanged words⟩ this instead of guessing a platform's limits. statusissaysonewhoofcanthreeconnect: live meansanyone can connect;anyone; beta means the channel is in platform review ⟨11 unchanged words⟩ yet, though publishing works normally for those accounts; bridge means it needs the user's browser extension running; soon means it is not connectable at all. ⟨52 unchanged words⟩
Added "delete_reply" (28 tools before, 29 now)
Delete a reply — Delete a comment for good. This is not hide_reply: it cannot be undone. What it reaches differs by channel and list_platforms says which ones support it at all. On Instagram and Facebook it removes anyone's comment on your post; on Threads and Bluesky a reply is itself a post, so it only removes replies the connected account wrote. Prefer hide_reply when the person just wants it out of sight.
Changed the definition of "publish", "retry_post" and "update_post"
⟨51 unchanged words⟩ that, the draft is already in Naver. Naver Blog asks which form to publish with, every post: if options.naver_blog.form is missing this returns 400 naver_form_required with forms (id, name, when). The form is the house style to use when the person has no style of their own, so read the conversation first. If they dictated the structure or you laid it out yourself, pass options.naver_blog.layout: "as-is" and the markdown goes out unchanged. Otherwise show them the forms (naver_layout with forms: "all" lays every one out side by side) and call again with the one they pick. Do not pick for them. Text limits differ per channel: Threads 500 characters ⟨78 unchanged words⟩ to 10 items in one post, YouTubehastakesno1carouselvideo andplaces up to 0no imagesin the post itself, Facebook groups up to 10 items in
Added "follow", "like" and "naver_layout"; changed the definition of "media_upload_status", "publish", "reply" and 2 more (25 tools before, 28 now)
⟨17 unchanged words⟩ link. Pass those ids to publish as mediaIds. Ready images also come back as image blocks so you can SEE each photo and place it in the right paragraph: previewIds[i] is the media id of the i-th image. Up to 20 images per call; pass offset to see the rest. A media with duplicateOf is the same bytes as that other id — use one of them.
{"properties":{"offset":{"description":"Skip this many images before returning previews (default 0).","type":"integer"},"token":{"description":"The token from media_upload_link. ⟨48 unchanged words⟩
Changed the definition of "publish"
⟨1271 unchanged words⟩ post instead.","type":"string"},"seoKeywords":{"description":"The search keywords this post targets, from keyword research (POST /v1/naver/keywords/judge): main is the one keyword the title leads with, subs (2-5) become the section titles one each. Not used when publishing; stored with the post and read by the SEO check.","properties":{"main":{"description":"The main keyword, verbatim in the title and the first line.","maxLength":100,"type":"string"},"measuredAt":{"description":"ISO 8601 date the numbers were measured.","type":"string"},"subs":{"description":"Sub keywords, one per section title.","items":{"description":"One sub keyword.","maxLength":100,"type":"string"},"maxItems":5,"type":"array"}},"type":"object"},"tags":{"description":"Up to 30 tags ⟨1290 unchanged words⟩
Changed the definition of "publish"
⟨2095 unchanged words⟩ We pass this through as you set it.Google can still restrict uploads from an API projectIfthatYouTubeisappliesundersomethingreviewelse,so ifthevideo ends up somewhere else youresultgetcarries a warningon the resultsaying whatYouTubeit
Added "publish_now" and "update_post"; changed the definition of "delete_post", "list_platforms", "list_posts" and 1 more (23 tools before, 25 now)
before
—after
Delete a postbefore
—after
Delete a post from the channel for good. This is not reversible, so confirm with the person first. Daily delete limits differ per channel: Threads 100 a day, Instagram only on accounts connected via Facebook, with no documented daily cap, YouTube 20 a day, Facebook 50 a day, Bluesky 35000 a day, Telegram only within 48 hours of publishing, with no documented daily cap, Naver Blog through the browser extension, with no documented daily cap, TikTok has no delete API; posts can only be removed in the app. On a scheduled or draft post nothing is on any channel yet, so this simply cancels it and removes our record. On Threads and YouTube this also works on posts written in the channel's own app, given the link. Instagram only lets us delete on accounts connected via Facebook: an account connected with Instagram login cannot be deleted through us at all, so tell the person to delete it in the Instagram app. Facebook only lets us delete Page posts this app published, so a post made in the Facebook app cannot be deleted through us.Added "get_publish_options"; changed the definition of "delete_post", "list_channel_posts", "media_presign" and 1 more (22 tools before, 23 now)
The earlier definition is no longer stored; only the fact of the change is.
The earlier definition is no longer stored; only the fact of the change is.
The earlier definition is no longer stored; only the fact of the change is.
The earlier definition is no longer stored; only the fact of the change is.
Definition not stored.
Certificate recorded, valid to 2026-11-30
Authorization not required
Changed the definition of "publish" and "retry_post"
The earlier definition is no longer stored; only the fact of the change is.
The earlier definition is no longer stored; only the fact of the change is.
Added "bridge_status" (21 tools before, 22 now)
Definition not stored.
First tool surface recorded: 21 tools (server version 1.0.0)
Showing the latest 65 events. The API returns up to 500 and filters by kind: ?kind=tool_surface_changed
{"properties":{"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"name":{"description" ⟨55 unchanged words⟩
{"properties":{"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"workspaceId":{"description" ⟨44 unchanged words⟩
{"properties":{"id":{"description":"Automation idfrom(af_…):list_automations.id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"version":{ ⟨61 unchanged words⟩
{"properties":{"id":{"description":"Contact id (ct_…): contact.id in list_conversations or read_conversation.","type":"string"},"refresh":{"description" ⟨55 unchanged words⟩
⟨2 unchanged words⟩ {"type":"string"},"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"limit":{"type" ⟨47 unchanged words⟩
{"properties":{"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"workspaceId":{"description" ⟨44 unchanged words⟩
⟨818 unchanged words⟩ ,"type":"object"},"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"name":{"type" ⟨52 unchanged words⟩
{"properties":{"id":{"description":"Conversation id (cv_…) from list_conversations.","type":"string"},"workspaceId":{"description" ⟨44 unchanged words⟩
{"properties":{"id":{"description":"TheAutomationautomation.id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"includeAnswered":{ ⟨114 unchanged words⟩
⟨2 unchanged words⟩ {"type":"boolean"},"id":{"description":"Automation id (af_…): id in list_automations, or data.id in the create_comment_to_dm, create_automation or duplicate_automation result.","type":"string"},"name":{"type" ⟨71 unchanged words⟩
⟨8 unchanged words⟩ the Naver Blog extension and how to wakeChromeits browser"}
⟨7 unchanged words⟩ on Naver's integrated search through the person's uplikaChromebrowser extension (it reads the public result page ⟨163 unchanged words⟩ check a draft against as their own layer, and weaker suggestions (grade B) to offer without checking, each with its evidence and caveat (they are not Naver's criteria). Which ⟨16 unchanged words⟩
⟨246 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨36 unchanged words⟩ research_naver_keywords, not proven keywords. The person's uplikaChromebrowser extension looks them up in the background when ⟨79 unchanged words⟩
⟨65 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨34 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨70 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨181 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨93 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨53 unchanged words⟩ the extension 0.7.2 or later in the person'sChrome;browser; otherwise you get extension_outdated. If the response carries `bridge`, `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨37 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨48 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨166 unchanged words⟩ a bridge object, read bridge.state: offline meansthattheChromebrowser with the extension is closed and the post is queued (up to 7 days); logged_out meansChromethat browser is on but not logged in to Naver; login_needed means the Naver login saved for that blog in thatChromebrowser is signed out, so its posts wait ⟨38 unchanged words⟩ it goes out. If the extension in thatChromebrowser is too old for what you asked ( ⟨6 unchanged words⟩ the installed and required versions. Uplika asks thatChromebrowser to update at the same moment, so ⟨891 unchanged words⟩
⟨138 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨115 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨76 unchanged words⟩ `bridge.nextStep` says what is needed: `run_wake` meansChromethe browser with the extension is closed (`bridge.browser` says which: Chrome, Edge or Whale), so tell the person `bridge.userMessage` with the `bridge.wake` command for their OS and retry once after they sayChromeit is open; `ask_user_login` / `ask_user_update` mean tell the ⟨8 unchanged words⟩
⟨43 unchanged words⟩ and checks every keyword through the person's uplikaChromebrowser extension in the background. Returns runId and etaSeconds ⟨74 unchanged words⟩
⟨123 unchanged words⟩ to research_naver_keywords instead. naver_autocomplete_paused and naver_autocomplete_busy carry retryAfterSeconds.Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨35 unchanged words⟩ list means nobody has measured it here yet.Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨103 unchanged words⟩ : the results of one check so far.ReturnsWith403blog,research_tool_disabledcards count every post of that blog Uplika has checked so far (earlier report cards, keyword checks withenableUrlthisifblog, and theperson1,has7 and 30 day rechecks of posts published through Uplika), notturnedonlythisthetoollastonscan;forlastScan.totalconnectors.isTellthethemsizeonceof the last scan. Of the five axes, search_title, consistency anddotopic_focusnotcomecallfromittheagainpostuntiltitles;theyai_citedsayanditsearch_exposediscomeon.from search checks.
⟨30 unchanged words⟩ Reports belong to the person, not a workspace.Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨163 unchanged words⟩ topic to put the post's subject in it.Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨76 unchanged words⟩ limit is how many posts, up to3050 (default2050). A report card for the same blog ⟨7 unchanged words⟩ instead of starting another (reused: true).Returns 403 research_tool_disabled with enableUrl ifUnlikethediagnose_naver_blog,personwhichhasreadsnottitlesturnedonly, thistoolsearchesoneachforpost'sconnectors.keywordTellthroughthemtheonceextension.andItdoalsonotrefreshescalltheittitleagainsummaryuntildiagnose_naver_blogtheyreturns,saywithoutitspendingisaon.diagnosis.
⟨15 unchanged words⟩ :{"description":"How many recent posts,1-30.1-50.","type":"number"},"workspaceId":{ ⟨45 unchanged words⟩
⟨153 unchanged words⟩ Saved to the person's search history (reportId).Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors. Tell them once and do not call it again until they say it is on.
⟨163 unchanged words⟩ topic to put the post's subject in it. The answer also carries citationRules: sentence rules Uplika measured on posts Naver's AI briefing quoted. They come even when no set forms (every keyword a wall or phantom), so a post written without a set can still follow them. Returns 403 research_tool_disabled with enableUrl if the person ⟨23 unchanged words⟩
⟨464 unchanged words⟩ you pass content, mediaIds or options. On Naver Blog, leave options.naver_blog.openType out of an edit to keep the post's current visibility; pass it only to change who can see the post. On a post that goes only to Naver Blog, ⟨337 unchanged words⟩
Check a keyword's Naver AI briefing and search results — Naver search only. Looks each keyword up on Naver's integrated search through the person's uplika Chrome extension (it reads the public result page in their own browser, about 3 seconds apart; no window opens) and returns, per keyword, whether an AI briefing answers it and in which form, the briefing's body sources (rank, site, title, how many answer paragraphs cite each) and image sources, where the blog posts sit in the integrated search results, and the related questions. Pass blog to mark that blog's posts (mine: true) and get its verdict per keyword (bodyRank, imageRank, citedParagraphs,…
Read recorded Naver search and AI briefing results — Reads what uplika has recorded about Naver search and AI briefings, without reading Naver again. With blog: the report card (cards: AI citation rate, search exposure rate, keywords with an AI briefing, cited posts; axes: five scores out of 10 on uplika's own scale with the raw value and threshold of each, not a Naver score; firstThing: the lowest axis; posts with their main keyword and latest verdict; events: when a post started or stopped being cited or shown). With post (a Naver post address, or the id of a post published through Uplika): that post's check history and events. With runId: th…
Start a Naver blog's search report card — Naver search only. Starts a report card for a Naver blog: reads its latest posts from the public RSS feed, picks each post's main keyword (from its title, or the keyword chosen when it was written in Uplika, or one the person set), and checks every keyword through the person's uplika Chrome extension in the background. Returns runId and etaSeconds right away and does not wait; read the result with get_naver_visibility (blog) when etaSeconds have passed. limit is how many posts, up to 30 (default 20). A report card for the same blog from the last 6 hours is returned instead of starting another…
Instagram only: find music or sounds for a reel — Instagram only. Lists audio you can put on an Instagram reel: music, or original sounds from other reels. Put the id you pick in options.instagram.audio.id when you call publish. Reels only. Works only for Instagram accounts connected via Facebook; an account connected with Instagram login answers not_supported, and the person has to reconnect it with Facebook. type is music (default) or original_sound. Without query you get what is trending for this account. With query, music comes only from Meta's royalty-free Sound Collection, so an artist or song name will not find that song; look through…
⟨2152 unchanged words⟩ API for deleting a post or reading comments.UntilAUplikadirectcompletespostTikTok'sgoesreviewoutawithdirectthepostprivacyLevelisyouvisiblepass; onlytoathepubliccreatorpost(SELF_ONLY)thatandTikTok has reviewed comes back withnoa link andnometrics. postMode draft sends the video to the ⟨222 unchanged words⟩ so we send no visibility on this path.Until Uplika completes TikTok's review, a direct post is visible only to the creator (SELF_ONLY).A draft is not on the profile until ⟨832 unchanged words⟩
⟨860 unchanged words⟩ as literal text, so we refuse instead. A photo line takes {.link=https://…} to make the photo a link (describe_grammar topic=media). This publishes immediately unless you pass scheduledAt (we ⟨243 unchanged words⟩
before
{"properties":{"accountIds":{"description":"Account ids from select_channels","items":{"type":"string"},"type":"array"},"automation":{"description":"Attach an automation to this post in the same call. template defaults to comment_to_dm; params are that template's params (see list_automation_templates) minus post, which is this post. Created as a draft unless enabled: true. Each target goes through the same checks as create_automation. A target whose channel the template does not cover (comment_to_dm on Threads or Naver Blog, any template on YouTube, Telegram, Bluesky or TikTok) is skipped and named in the response with the reason.","properties":{"enabled":{"type":"boolean"},"name":{"type":"string"},"params":{"type":"object"},"template":{"type":"string"}},"type":"object"},"batchId":{"description":"Optional tag (letters, digits, - or _) to group posts made together, for example the same text sent to two workspaces. Pass the same value on each call.","type":"string"},"content":{"description":"Post text","type":"string"},"draft":{"description":"true saves the post as a draft on uplika without sending anything. Use it when the person wants to review before it goes out. They finish it in the dashboard, or you call update_post and publish_now.","type":"boolean"},"mediaIds":{"description":"Media ids from media_presign (confirmed with media_complete), media_from_url or media_upload_link. On Naver Blog you can leave out ids the body already points at with media:<id>; we pick those up from the text.","items":{"type":"string"},"type":"array"},"options":{"description":"Per-channel settings, keyed by channel id. Only the channels you are posting to need an entry. Every channel takes content to override the shared text for that channel alone.","properties":{"bluesky":{"description":"Bluesky settings. Optional, but langs is worth setting on every post.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"langs":{"description":"The language of the text, 1-3 BCP-47 codes like [\"ko\"] or [\"en\", \"ko\"]. Feed generators filter on this, so without it the post never appears in language-scoped feeds. Bluesky has no post editing, so this cannot be fixed after publishing. Set it to the language the text is actually written in.","items":{"description":"One BCP-47 code.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"maxItems":3,"type":"array"}},"type":"object"},"facebook":{"description":"Facebook Page settings. Optional: plain text publishes without it.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. A Facebook story is rejected if you send any text: publish the story with no content, and put the words in a feed post instead. A story also takes exactly one image or video and no first comment.","enum":["feed","story","reel"],"type":"string"},"firstComment":{"description":"A comment we post right after publishing. Not on stories, and not on scheduled posts. If it fails the post still goes up, with a warning.","type":"string"},"link":{"description":"Makes a link post with a preview card. Cannot be combined with media.","type":"string"},"scheduledPublishTime":{"description":"ISO 8601 time to publish a feed post later, 10 minutes to 28 days from now. Feed posts only, not reels or stories. Cannot be combined with firstComment (a scheduled post is not live yet, so nothing to comment on). This is Facebook's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"title":{"description":"Reel title, separate from the caption.","type":"string"}},"type":"object"},"instagram":{"description":"Instagram settings. Optional: a plain post with media works without it.","properties":{"collaborators":{"description":"Up to 3 public professional accounts to invite as collaborators. They appear only after accepting. Not on stories.","items":{"description":"A public professional account's username, without @.","type":"string"},"maxItems":3,"type":"array"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. Stories show no caption and take one image or video.","enum":["feed","story","reel"],"type":"string"},"coverMediaId":{"description":"Media id of a JPEG up to 8MB to use as the reel cover. Reels only.","type":"string"},"firstComment":{"description":"A comment we post right after publishing, often used for hashtags. Not on stories. If it fails the post still goes up, with a warning.","type":"string"},"isAiGenerated":{"description":"Set true to label the media as AI-generated on Instagram. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"shareToFeed":{"description":"Reels only. false keeps the reel out of the main feed.","type":"boolean"},"thumbOffsetMs":{"description":"Reel cover frame in milliseconds. Ignored when coverMediaId is set.","minimum":0,"type":"number"},"userTags":{"description":"Tag accounts on the media. Photos need x and y between 0 and 1; videos take the username alone. mediaIndex picks the carousel slide.","items":{"description":"One tagged account.","properties":{"mediaIndex":{"description":"Which carousel slide, from 0.","type":"number"},"username":{"description":"Username without @.","type":"string"},"x":{"description":"0 to 1 from the left. Photos only.","type":"number"},"y":{"description":"0 to 1 from the top. Photos only.","type":"number"}},"required":["username"],"type":"object"},"type":"array"}},"type":"object"},"naver_blog":{"description":"Naver Blog settings. Required when a target is a Naver Blog account. Naver Blog has no official API: the post is written by the user's browser extension, so it goes out only while that browser is open. list_accounts tells you whether the extension is online; if it is offline the post queues for up to 7 days and you should say so. The body is Markdown, plus Naver-only markup for things Markdown has no word for (highlighting, styled tables, collages, maps, events): call describe_grammar with platform naver_blog for the syntax and its traps. Reference uploaded media inline with ``; ids you reference this way do not have to repeat in mediaIds, and media you pass but never reference goes at the end. When other channels are in the same post, put the Markdown in options.naver_blog.content so the shared content stays plain text.","properties":{"alignCenter":{"description":"Center every paragraph and image that has no alignment of its own. A photo line's own {.left} / {.center} / {.right} wins.","type":"boolean"},"category":{"description":"The category name instead of its id, exactly as it appears in naverBlog.categories from list_accounts. If the blog does not have it you get naver_category_unknown with the list to pick from; if the person just created it, call refresh_account first.","type":"string"},"categoryId":{"description":"The id of a category on the connected blog. list_accounts returns them under naverBlog.categories. Give this or category (the name); there is no default, and without one Naver files the post under the wrong board. Not needed and ignored when draftOnly is true (a draft has no board until it is published).","type":"string"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content. On Naver Blog this is where the Markdown body goes when the other channels get plain text.","type":"string"},"draftOnly":{"description":"Save as a draft in Naver instead of publishing. The user finishes it by hand, or you publish it later with publish_naver_draft. categoryId, tags and openType are not stored on a draft; pass them when you publish it. Different from draft on the post, which keeps the whole post on uplika.","type":"boolean"},"fontFamily":{"description":"Body font, one of nanumgothic, nanummyeongjo, nanumbarungothic, nanumsquare, maruburi. Omit for the editor default.","enum":["nanumgothic","nanummyeongjo","nanumbarungothic","nanumsquare","maruburi"],"type":"string"},"fontSize":{"description":"Body font size in px, one of the editor's sizes (11-38). Omit for the editor default.","enum":["11","13","15","16","19","24","28","30","34","38"],"maximum":34,"minimum":10,"type":"number"},"form":{"description":"Form preset for the template layout (default photo-story). GET /v1/naver/forms lists the presets; naver_layout with forms: \"all\" shows them side by side.","enum":["bubble-info","divider-tutorial","photo-review","place-list","news-event","table-explain","mood-review","photo-story"],"type":"string"},"imageSize":{"description":"Size of every single photo that has none of its own: fit is the document width, small is the editor's Small (about three quarters). A photo line's own {.fit} / {.small} wins. Leave it out for the default: document width for photos wider than 693px, original width for narrower ones. @group photos are always document width. Needs uplika extension 0.7.13 or later; with an older one the post still goes out and, if fit was asked for, a warning says so.","enum":["fit","small"],"type":"string"},"layout":{"description":"template (default) reshapes the body into the blog's house form before it goes out: #/## titles become underlined quote headings, a thin rule sits between sections, and photos you did not place with media: references go one per section (leftovers pair up at the end). Text is never changed. Call naver_layout first to see the result. Use as-is when the person dictated the structure or you placed everything yourself.","enum":["template","as-is"],"type":"string"},"openType":{"description":"Who can see it. public (default), neighbor, mutual (mutual neighbors only), or private. Ignored when draftOnly is true.","enum":["public","neighbor","mutual","private"],"type":"string"},"scheduledAt":{"description":"Same as scheduledAt on the post: ISO 8601 with a timezone offset, 10 minutes to a year out. Uplika holds the post and sends it to Naver at that time; Naver's own reservation is not used. Prefer scheduledAt on the post. Not allowed together with draftOnly or on a post that also targets other channels.","type":"string"},"seoKeywords":{"description":"The search keywords this post targets, from keyword research (POST /v1/naver/keywords/judge): main is the one keyword the title leads with, subs (2-5) become the section titles one each. Not used when publishing; stored with the post and read by the SEO check.","properties":{"main":{"description":"The main keyword, verbatim in the title and the first line.","maxLength":100,"type":"string"},"measuredAt":{"description":"ISO 8601 date the numbers were measured.","type":"string"},"subs":{"description":"Sub keywords, one per section title.","items":{"description":"One sub keyword.","maxLength":100,"type":"string"},"maxItems":5,"type":"array"}},"type":"object"},"tags":{"description":"Up to 30 tags without the # sign and without spaces. Ignored when draftOnly is true.","items":{"description":"One tag without # and without spaces.","type":"string"},"maxItems":30,"type":"array"},"tempLogNo":{"description":"Set by publish_naver_draft: the Naver temp-saved draft to publish as it is. The body is ignored; the extension loads that draft in the editor and presses publish.","type":"string"},"title":{"description":"Required. The post title, up to 100 characters.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"},"telegram":{"description":"Telegram has no channel-specific settings. Note the caption limit: with media attached the text is capped at 1,024 characters instead of 4,096.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"}},"type":"object"},"threads":{"description":"Threads settings. Optional.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"topicTag":{"description":"One topic to tag the post with, like a category. Threads takes exactly one and rejects periods and ampersands in it. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","maxLength":64,"type":"string"}},"type":"object"},"tiktok":{"description":"TikTok settings. privacyLevel is REQUIRED for a normal post and has no default on purpose: TikTok's rules say the person must choose visibility deliberately, so we ask for it instead of guessing. Call get_publish_options first to see which values this account may use right now, whether it can post at all, and its video length limit. TikTok has no text-only posts, cannot mix a video and photos, and has no API for deleting a post or reading comments. Until Uplika completes TikTok's review a direct post is visible only to the creator (SELF_ONLY) and comes back with no link and no metrics. postMode draft sends the video to the creator's TikTok inbox, where they choose who can see it.","properties":{"brandContentToggle":{"description":"Declare paid partnership content (\"Branded Content\"). TikTok does not allow branded content to be private, so this cannot be combined with privacyLevel SELF_ONLY.","type":"boolean"},"brandOrganicToggle":{"description":"Declare that the post promotes the creator's own brand (\"Your Brand\"). Leave it out rather than sending false: not declaring and declaring 'no' are different statements.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"disableComment":{"description":"Turn comments off for this post. Cannot be set to false if the creator disabled comments account-wide.","type":"boolean"},"disableDuet":{"description":"Turn duets off for this post. Video posts only.","type":"boolean"},"disableStitch":{"description":"Turn stitches off for this post. Video posts only.","type":"boolean"},"isAigc":{"description":"Declare that the content was generated by AI. Worth setting when you made the video or images. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"postMode":{"description":"direct (default) posts to the profile. draft sends it to the creator's TikTok inbox; they open the notification and finish it in the app, choosing who can see it themselves, so we send no visibility on this path. Until Uplika completes TikTok's review, a direct post is visible only to the creator (SELF_ONLY). A draft is not on the profile until the person finishes it, so there is no link and no metrics in the meantime.","enum":["direct","draft"],"type":"string"},"privacyLevel":{"description":"Required unless postMode is draft. One of the values that get_publish_options returns for this account, e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. The list differs per account, so do not hard-code it.","type":"string"},"title":{"description":"Required for a photo post, up to 90 characters. A video post has no title field at all and passing one is rejected; a video's text comes from content.","maxLength":90,"type":"string"},"videoCoverTimestampMs":{"description":"Which frame to use as the cover, in milliseconds into the video. TikTok does not accept a cover image file, only a timestamp.","minimum":0,"type":"number"}},"type":"object"},"youtube":{"description":"Required when a YouTube account is in accountIds. The post content becomes the video description.","properties":{"categoryId":{"description":"YouTube category id. Defaults to 22 (People & Blogs). Leave it out unless the person named a category.","type":"string"},"containsSyntheticMedia":{"description":"Set true when the video contains realistic altered or synthetic content, including AI generated footage of real-looking people, places or events. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"madeForKids":{"description":"Whether this video is directed at children. This is a legal declaration about someone else's channel. Leave it out unless the person tells you, and the channel's own default applies.","type":"boolean"},"privacyStatus":{"description":"Defaults to private. We pass this through as you set it. If YouTube applies something else, the result carries a warning saying what it actually applied.","enum":["public","unlisted","private"],"type":"string"},"publishAt":{"description":"ISO 8601 time to make the video public. Only works with privacyStatus private, and only on a video that was never public. This is YouTube's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"tags":{"description":"Search tags. 500 characters across all of them; a tag with a space costs two extra for the quotes YouTube adds.","items":{"description":"One search tag.","type":"string"},"type":"array"},"thumbnailMediaId":{"description":"Media id of a JPEG or PNG up to 2MB to use as the thumbnail. Long-form only: YouTube does not take custom thumbnails on Shorts, and a vertical video of three minutes or less becomes a Short. The channel also has to be verified before YouTube accepts one.","type":"string"},"title":{"description":"Video title, up to 100 characters. Required. Cannot contain < or >.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"}},"type":"object"},"scheduledAt":{"description":"ISO 8601 time with a timezone offset (2026-09-20T09:00:00+09:00) to send the post at, on every channel. 10 minutes to 365 days from now. We keep the post as scheduled until then; the person can still change or cancel it.","type":"string"},"threadItems":{"description":"Split a long post into a chain instead of calling publish and then reply. The first item is the root and the rest become replies under it, in order. We handle the ordering and the waiting. Each item obeys the character limit on its own. Pass either content or threadItems, not both. If a later item fails, the ones already up stay up and the response tells you where to resume.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"topicTag":{"description":"One topic to tag the post with, like a category. Only some channels take one, and those reject periods and ampersands in it. If any channel in accountIds does not take topics the whole call is refused, so publish to it separately. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","type":"string"},"wait":{"description":"Hold the response until the post is really out. Text waits up to 10 seconds, posts with media up to 75 seconds. If it is still going after that you get the usual publishing response and should poll get_post. Defaults to false. Ignored with scheduledAt or draft.","type":"boolean"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["accountIds"],"type":"object"}after
{"properties":{"accountIds":{"description":"Account ids from select_channels","items":{"type":"string"},"type":"array"},"automation":{"description":"Attach an automation to this post in the same call. template defaults to comment_to_dm; params are that template's params (see list_automation_templates) minus post, which is this post. Created as a draft unless enabled: true. Each target goes through the same checks as create_automation. A target whose channel the template does not cover (comment_to_dm on Threads or Naver Blog, any template on YouTube, Telegram, Bluesky or TikTok) is skipped and named in the response with the reason.","properties":{"enabled":{"type":"boolean"},"name":{"type":"string"},"params":{"type":"object"},"template":{"type":"string"}},"type":"object"},"batchId":{"description":"Optional tag (letters, digits, - or _) to group posts made together, for example the same text sent to two workspaces. Pass the same value on each call.","type":"string"},"content":{"description":"Post text","type":"string"},"draft":{"description":"true saves the post as a draft on uplika without sending anything. Use it when the person wants to review before it goes out. They finish it in the dashboard, or you call update_post and publish_now.","type":"boolean"},"mediaIds":{"description":"Media ids from media_presign (confirmed with media_complete), media_from_url or media_upload_link. On Naver Blog you can leave out ids the body already points at with media:<id>; we pick those up from the text.","items":{"type":"string"},"type":"array"},"options":{"description":"Per-channel settings, keyed by channel id. Only the channels you are posting to need an entry. Every channel takes content to override the shared text for that channel alone.","properties":{"bluesky":{"description":"Bluesky settings. Optional, but langs is worth setting on every post.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"langs":{"description":"The language of the text, 1-3 BCP-47 codes like [\"ko\"] or [\"en\", \"ko\"]. Feed generators filter on this, so without it the post never appears in language-scoped feeds. Bluesky has no post editing, so this cannot be fixed after publishing. Set it to the language the text is actually written in.","items":{"description":"One BCP-47 code.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"maxItems":3,"type":"array"}},"type":"object"},"facebook":{"description":"Facebook Page settings. Optional: plain text publishes without it.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. A Facebook story is rejected if you send any text: publish the story with no content, and put the words in a feed post instead. A story also takes exactly one image or video and no first comment.","enum":["feed","story","reel"],"type":"string"},"feedTargeting":{"description":"Preferred audience for a Facebook text or link post. A hint, not a limit: Facebook may show the post more to these people, others can still see it, and the effect is not guaranteed. Facebook does not keep it on photo posts or reels (we checked), so those are refused. It cannot be changed after the post is published.","properties":{"countries":{"description":"Countries to aim the post at, for example [\"US\", \"GB\"].","items":{"description":"ISO 3166-1 two-letter country code in capitals, like US.","pattern":"^[A-Z]{2}$","type":"string"},"maxItems":25,"type":"array"}},"type":"object"},"firstComment":{"description":"A comment we post right after publishing. Not on stories, and not on scheduled posts. If it fails the post still goes up, with a warning.","type":"string"},"link":{"description":"Makes a link post with a preview card. Cannot be combined with media.","type":"string"},"scheduledPublishTime":{"description":"ISO 8601 time to publish a feed post later, 10 minutes to 28 days from now. Feed posts only, not reels or stories. Cannot be combined with firstComment (a scheduled post is not live yet, so nothing to comment on). This is Facebook's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"title":{"description":"Reel title, separate from the caption.","type":"string"}},"type":"object"},"instagram":{"description":"Instagram settings. Optional: a plain post with media works without it.","properties":{"collaborators":{"description":"Up to 3 public professional accounts to invite as collaborators. They appear only after accepting. Not on stories.","items":{"description":"A public professional account's username, without @.","type":"string"},"maxItems":3,"type":"array"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. Stories show no caption and take one image or video.","enum":["feed","story","reel"],"type":"string"},"coverMediaId":{"description":"Media id of a JPEG up to 8MB to use as the reel cover. Reels only.","type":"string"},"firstComment":{"description":"A comment we post right after publishing, often used for hashtags. Not on stories. If it fails the post still goes up, with a warning.","type":"string"},"isAiGenerated":{"description":"Set true to label the media as AI-generated on Instagram. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"shareToFeed":{"description":"Reels only. false keeps the reel out of the main feed.","type":"boolean"},"thumbOffsetMs":{"description":"Reel cover frame in milliseconds. Ignored when coverMediaId is set.","minimum":0,"type":"number"},"userTags":{"description":"Tag accounts on the media. Photos need x and y between 0 and 1; videos take the username alone. mediaIndex picks the carousel slide.","items":{"description":"One tagged account.","properties":{"mediaIndex":{"description":"Which carousel slide, from 0.","type":"number"},"username":{"description":"Username without @.","type":"string"},"x":{"description":"0 to 1 from the left. Photos only.","type":"number"},"y":{"description":"0 to 1 from the top. Photos only.","type":"number"}},"required":["username"],"type":"object"},"type":"array"}},"type":"object"},"naver_blog":{"description":"Naver Blog settings. Required when a target is a Naver Blog account. Naver Blog has no official API: the post is written by the user's browser extension, so it goes out only while that browser is open. list_accounts tells you whether the extension is online; if it is offline the post queues for up to 7 days and you should say so. The body is Markdown, plus Naver-only markup for things Markdown has no word for (highlighting, styled tables, collages, maps, events): call describe_grammar with platform naver_blog for the syntax and its traps. Reference uploaded media inline with ``; ids you reference this way do not have to repeat in mediaIds, and media you pass but never reference goes at the end. When other channels are in the same post, put the Markdown in options.naver_blog.content so the shared content stays plain text.","properties":{"alignCenter":{"description":"Center every paragraph and image that has no alignment of its own. A photo line's own {.left} / {.center} / {.right} wins.","type":"boolean"},"category":{"description":"The category name instead of its id, exactly as it appears in naverBlog.categories from list_accounts. If the blog does not have it you get naver_category_unknown with the list to pick from; if the person just created it, call refresh_account first.","type":"string"},"categoryId":{"description":"The id of a category on the connected blog. list_accounts returns them under naverBlog.categories. Give this or category (the name); there is no default, and without one Naver files the post under the wrong board. Not needed and ignored when draftOnly is true (a draft has no board until it is published).","type":"string"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content. On Naver Blog this is where the Markdown body goes when the other channels get plain text.","type":"string"},"draftOnly":{"description":"Save as a draft in Naver instead of publishing. The user finishes it by hand, or you publish it later with publish_naver_draft. categoryId, tags and openType are not stored on a draft; pass them when you publish it. Different from draft on the post, which keeps the whole post on uplika.","type":"boolean"},"fontFamily":{"description":"Body font, one of nanumgothic, nanummyeongjo, nanumbarungothic, nanumsquare, maruburi. Omit for the editor default.","enum":["nanumgothic","nanummyeongjo","nanumbarungothic","nanumsquare","maruburi"],"type":"string"},"fontSize":{"description":"Body font size in px, one of the editor's sizes (11-38). Omit for the editor default.","enum":["11","13","15","16","19","24","28","30","34","38"],"maximum":34,"minimum":10,"type":"number"},"form":{"description":"Form preset for the template layout (default photo-story). GET /v1/naver/forms lists the presets; naver_layout with forms: \"all\" shows them side by side.","enum":["bubble-info","divider-tutorial","photo-review","place-list","news-event","table-explain","mood-review","photo-story"],"type":"string"},"imageSize":{"description":"Size of every single photo that has none of its own: fit is the document width, small is the editor's Small (about three quarters). A photo line's own {.fit} / {.small} wins. Leave it out for the default: document width for photos wider than 693px, original width for narrower ones. @group photos are always document width. Needs uplika extension 0.7.13 or later; with an older one the post still goes out and, if fit was asked for, a warning says so.","enum":["fit","small"],"type":"string"},"layout":{"description":"template (default) reshapes the body into the blog's house form before it goes out: #/## titles become underlined quote headings, a thin rule sits between sections, and photos you did not place with media: references go one per section (leftovers pair up at the end). Text is never changed. Call naver_layout first to see the result. Use as-is when the person dictated the structure or you placed everything yourself.","enum":["template","as-is"],"type":"string"},"openType":{"description":"Who can see it. public (default), neighbor, mutual (mutual neighbors only), or private. Ignored when draftOnly is true.","enum":["public","neighbor","mutual","private"],"type":"string"},"scheduledAt":{"description":"Same as scheduledAt on the post: ISO 8601 with a timezone offset, 10 minutes to a year out. Uplika holds the post and sends it to Naver at that time; Naver's own reservation is not used. Prefer scheduledAt on the post. Not allowed together with draftOnly or on a post that also targets other channels.","type":"string"},"seoKeywords":{"description":"The search keywords this post targets, from keyword research (POST /v1/naver/keywords/judge): main is the one keyword the title leads with, subs (2-5) become the section titles one each. Not used when publishing; stored with the post and read by the SEO check.","properties":{"main":{"description":"The main keyword, verbatim in the title and the first line.","maxLength":100,"type":"string"},"measuredAt":{"description":"ISO 8601 date the numbers were measured.","type":"string"},"subs":{"description":"Sub keywords, one per section title.","items":{"description":"One sub keyword.","maxLength":100,"type":"string"},"maxItems":5,"type":"array"}},"type":"object"},"tags":{"description":"Up to 30 tags without the # sign and without spaces. Ignored when draftOnly is true.","items":{"description":"One tag without # and without spaces.","type":"string"},"maxItems":30,"type":"array"},"tempLogNo":{"description":"Set by publish_naver_draft: the Naver temp-saved draft to publish as it is. The body is ignored; the extension loads that draft in the editor and presses publish.","type":"string"},"title":{"description":"Required. The post title, up to 100 characters.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"},"telegram":{"description":"Telegram has no channel-specific settings. Note the caption limit: with media attached the text is capped at 1,024 characters instead of 4,096.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"}},"type":"object"},"threads":{"description":"Threads settings. Optional.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"topicTag":{"description":"One topic to tag the post with, like a category. Threads takes exactly one and rejects periods and ampersands in it. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","maxLength":64,"type":"string"}},"type":"object"},"tiktok":{"description":"TikTok settings. privacyLevel is REQUIRED for a normal post and has no default on purpose: TikTok's rules say the person must choose visibility deliberately, so we ask for it instead of guessing. Call get_publish_options first to see which values this account may use right now, whether it can post at all, and its video length limit. TikTok has no text-only posts, cannot mix a video and photos, and has no API for deleting a post or reading comments. Until Uplika completes TikTok's review a direct post is visible only to the creator (SELF_ONLY) and comes back with no link and no metrics. postMode draft sends the video to the creator's TikTok inbox, where they choose who can see it.","properties":{"brandContentToggle":{"description":"Declare paid partnership content (\"Branded Content\"). TikTok does not allow branded content to be private, so this cannot be combined with privacyLevel SELF_ONLY.","type":"boolean"},"brandOrganicToggle":{"description":"Declare that the post promotes the creator's own brand (\"Your Brand\"). Leave it out rather than sending false: not declaring and declaring 'no' are different statements.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"disableComment":{"description":"Turn comments off for this post. Cannot be set to false if the creator disabled comments account-wide.","type":"boolean"},"disableDuet":{"description":"Turn duets off for this post. Video posts only.","type":"boolean"},"disableStitch":{"description":"Turn stitches off for this post. Video posts only.","type":"boolean"},"isAigc":{"description":"Declare that the content was generated by AI. Worth setting when you made the video or images. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"postMode":{"description":"direct (default) posts to the profile. draft sends it to the creator's TikTok inbox; they open the notification and finish it in the app, choosing who can see it themselves, so we send no visibility on this path. Until Uplika completes TikTok's review, a direct post is visible only to the creator (SELF_ONLY). A draft is not on the profile until the person finishes it, so there is no link and no metrics in the meantime.","enum":["direct","draft"],"type":"string"},"privacyLevel":{"description":"Required unless postMode is draft. One of the values that get_publish_options returns for this account, e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. The list differs per account, so do not hard-code it.","type":"string"},"title":{"description":"Required for a photo post, up to 90 characters. A video post has no title field at all and passing one is rejected; a video's text comes from content.","maxLength":90,"type":"string"},"videoCoverTimestampMs":{"description":"Which frame to use as the cover, in milliseconds into the video. TikTok does not accept a cover image file, only a timestamp.","minimum":0,"type":"number"}},"type":"object"},"youtube":{"description":"Required when a YouTube account is in accountIds. The post content becomes the video description.","properties":{"categoryId":{"description":"YouTube category id. Defaults to 22 (People & Blogs). Leave it out unless the person named a category.","type":"string"},"containsSyntheticMedia":{"description":"Set true when the video contains realistic altered or synthetic content, including AI generated footage of real-looking people, places or events. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"defaultLanguage":{"description":"The language the title and description are written in, as one BCP-47 code (for example en-US or ko). Leave it out and none is sent. The spoken language (YouTube Studio's Video language) cannot be set through the API.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"madeForKids":{"description":"Whether this video is directed at children. This is a legal declaration about someone else's channel. Leave it out unless the person tells you, and the channel's own default applies. If the channel has not set its audience either, YouTube Studio will not save other edits to this video until someone picks one, so ask the person.","type":"boolean"},"privacyStatus":{"description":"Defaults to private. We pass this through as you set it. If YouTube applies something else, the result carries a warning saying what it actually applied.","enum":["public","unlisted","private"],"type":"string"},"publishAt":{"description":"ISO 8601 time to make the video public. Only works with privacyStatus private, and only on a video that was never public. This is YouTube's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"tags":{"description":"Search tags. 500 characters across all of them; a tag with a space costs two extra for the quotes YouTube adds.","items":{"description":"One search tag.","type":"string"},"type":"array"},"thumbnailMediaId":{"description":"Media id of a JPEG or PNG up to 2MB to use as the thumbnail. Long-form only: YouTube does not take custom thumbnails on Shorts, and a vertical video of three minutes or less becomes a Short. The channel also has to be verified before YouTube accepts one.","type":"string"},"title":{"description":"Video title, up to 100 characters. Required. Cannot contain < or >.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"}},"type":"object"},"scheduledAt":{"description":"ISO 8601 time with a timezone offset (2026-09-20T09:00:00+09:00) to send the post at, on every channel. 10 minutes to 365 days from now. We keep the post as scheduled until then; the person can still change or cancel it.","type":"string"},"threadItems":{"description":"Split a long post into a chain instead of calling publish and then reply. The first item is the root and the rest become replies under it, in order. We handle the ordering and the waiting. Each item obeys the character limit on its own. Pass either content or threadItems, not both. If a later item fails, the ones already up stay up and the response tells you where to resume.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"topicTag":{"description":"One topic to tag the post with, like a category. Only some channels take one, and those reject periods and ampersands in it. If any channel in accountIds does not take topics the whole call is refused, so publish to it separately. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","type":"string"},"wait":{"description":"Hold the response until the post is really out. Text waits up to 10 seconds, posts with media up to 75 seconds. If it is still going after that you get the usual publishing response and should poll get_post. Defaults to false. Ignored with scheduledAt or draft.","type":"boolean"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["accountIds"],"type":"object"}⟨786 unchanged words⟩ 75 seconds), or read it with get_post. If the account that published the post was disconnected, this returns 409 account_disconnected; reconnect the same account and the post id comes back.
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"CheckTikTokwhatonly:acreatorchannelsettingsneedsto check before publishing"}
⟨142 unchanged words⟩ A loop must pass through such a wait. A comment trigger's post.postId takes the post's link too; the server resolves it to the uplika post id. Validation problems come back with paths. Changing a ⟨8 unchanged words⟩
⟨688 unchanged words⟩ | \"any\" | \"next\". postId may also be the post's link (the server resolves it to the uplika post id when saving). \"next\" binds to the next post you ⟨155 unchanged words⟩
⟨671 unchanged words⟩ | \"any\" | \"next\". postId may also be the post's link (the server resolves it to the uplika post id when saving). \"next\" binds to the next post you ⟨145 unchanged words⟩
⟨37 unchanged words⟩ next_post_taken until the first binds to a post. Only one message flow without keywords (a default reply or AI replies) can be live per account, or one message would get two answers: enabling a second is refused with automation_catch_all_taken, which names the live one. Refuses with reconsent_required if the account does not ⟨50 unchanged words⟩
⟨43 unchanged words⟩ a link or file inside the messaging window. AI-written comment replies are comment_public_reply with replyMode "ai" (and comment_to_dm's public reply with publicReplyMode "ai"); the old template id ai_comment_reply still works and the answer says renamedFrom.
⟨41 unchanged words⟩ Created as a draft unless enabled: true.TargetsEachontargetchannelsgoeswithoutthroughcommentstheorsameDMschecks as create_automation. A target whose channel the template does not cover (YouTube,comment_to_dmTelegram,on Threads or Naver Blog,Blueskyany template on YouTube, Telegram, Bluesky or TikTok)areis skipped and named in theresponse.response with the reason.","properties":{"enabled":{"type": ⟨3003 unchanged words⟩
⟨734 unchanged words⟩ :"string"},"type":"array"},"publicReplyAi":{"description":"Instead of publicReply: the AI writes the public reply for each comment, after the flow ran. Told a DM was sent only when the private reply went out.","properties":{"instruction":{"type":"string"}},"required":["instruction"],"type":"object"},"ref":{"type":"string"},"tag" ⟨79 unchanged words⟩
⟨717 unchanged words⟩ :"string"},"type":"array"},"publicReplyAi":{"description":"Instead of publicReply: the AI writes the public reply for each comment, after the flow ran. Told a DM was sent only when the private reply went out.","properties":{"instruction":{"type":"string"}},"required":["instruction"],"type":"object"},"ref":{"type":"string"},"tag" ⟨69 unchanged words⟩
⟨189 unchanged words⟩ Chrome is on but not logged in toNaver.Naver; login_needed means the Naver login saved for that blog in that Chrome is signed out, so its posts wait until the person logs in again from the dashboard. Either way relay bridge.userMessage to the person word ⟨799 unchanged words⟩
⟨168 unchanged words⟩ only if the draft is not there. If a Naver reply did not confirm in time (errorCode naver_reply_unknown), this returns 409 naver_reply_unknown and retries nothing; look at the comment on the blog first and call again with force: true only if the reply is not there. If another post with the same content and files ⟨93 unchanged words⟩
⟨14 unchanged words⟩ after list_naver_drafts shows the draft is not there) or naver_reply_unknown (pass it only after the comment on the blog shows no such reply), or even though another post with the ⟨107 unchanged words⟩
⟨131 unchanged words⟩ carries bridge with state (online, offline, logged_out, login_needed), userMessage to relay to the person, ⟨12 unchanged words⟩
{"properties":{"detachTemplate":{"description":"Only for a flow that still has its template form: true turns it into a canvas-only flow the form can no longer open. Default false.","type":"boolean"},"doc":{"description":"One automation flow. triggers ⟨784 unchanged words⟩
⟨80 unchanged words⟩ , with the same checks as enable_automation). Instagram follow check: requireFollow, notFollowingMessage, recheckTitle. A flow that was edited on the canvas ⟨23 unchanged words⟩
⟨5 unchanged words⟩ from list_automations.","type":"string"},"version":{"description":"A saved version number from list_automation_versions. Leave out for the current one.","type":"integer"},"workspaceId":{"description":"Which workspace this is ⟨40 unchanged words⟩
⟨9 unchanged words⟩ is live or a draft, triggers, runcount.count, and templateParams when the flow still has its template shape. Filter by accountId, status (draft or live) or templateId. Use get_automation to read one flow's document.
{"properties":{"accountId":{"description":"Only automations on this connected account.","type":"string"},"status":{"description":"draft = not enabled, live = enabled.","enum":["draft","live"],"type":"string"},"templateId":{"description":"Only automations made from this template (list_automation_templates).","type":"string"},"workspaceId":{"description":"Which workspace this is ⟨38 unchanged words⟩
⟨90 unchanged words⟩ 20:1. Any image pixel width is fine. Documents for DM automations only (deliver.mediaId), not for posts: application/pdf, application/x-hwp, application/hwp+zip (.pdf, .hwp, .hwpx), up to 25MB. If you can see the image, write altText ⟨25 unchanged words⟩
⟨86 unchanged words⟩ or image/gif or video/mp4 or video/quicktime or video/webm or application/pdf or application/x-hwp or application/hwp+zip","type":"string"},"fileName":{ ⟨70 unchanged words⟩
⟨65 unchanged words⟩ the file already has a public https address. The page also takes PDF and HWP/HWPX documents, for a DM automation's file (deliver.mediaId); a post cannot carry them.
Delete an automation — Delete an automation and its run history. Cannot be undone. A live automation is refused with automation_live: disable it first, or pass force: true after the person confirms, because deleting it stops what is going out to people.
Copy an automation as a draft — Copy an automation as a new draft with the same triggers and settings, optionally with a new name. Runs and versions are not copied; a copy bound to a specific post keeps that post, a copy of a next-post flow waits for the next post again when enabled.
List an automation's saved versions — Saved versions of one automation (every put_automation or update_automation adds one). Read one with get_automation and version.
Edit an automation's template settings — Edit an automation that still has its template shape, without rebuilding the whole document: pass only the template params you want to change (the same params as create_automation / create_comment_to_dm). Top-level fields are replaced, object fields such as deliver merge one level deep (deliver.text alone keeps deliver.links), null removes a field, arrays are replaced whole. Read it first with get_automation and pass its version; a stale version answers version_conflict. Also renames (name) and turns it on or off (enabled, with the same checks as enable_automation). A flow that was edited on …
Check an automation document — Check a flow document against the rules for one account without saving it: node shapes, references, the 24-hour window, and features still in Meta review. Answers problems with paths; an empty list means put_automation would accept it.
⟨57 unchanged words⟩ text, up to three link buttons, and/oraonemediafileid.(fileUrl or mediaId).","properties":{"fileUrl":{"description":"Public https link to the file itself, sent as-is. Checked once on save.","type":"string"},"link":{"description":"Legacy: one url appended to ⟨27 unchanged words⟩ ,"type":"array"},"mediaId":{"description":"A file uploaded to Uplika media, for a file with no public link.","type":"string"},"mediaKind":{"description":"Filled in from the file when you pass fileUrl.","enum":["file","image","video" ⟨241 unchanged words⟩
Look up why something did not work — Uplika's troubleshooting answers: why something did not work and what the person can do about it. Call it with the error code you got (code), or with a short question in any language (query), when a call fails, when an automation did not react or a DM did not arrive, or when the person asks why something happened. Without arguments it lists every question with its id; pass id for one answer in full. Each answer has the steps, links and a url to the same answer on uplika.com that you can give the person.
⟨15 unchanged words⟩ kind mention. Reply with send_dm, which posts publicly. Mentions need a permission of their own: when no connected Threads account holds it the list is empty and the response carries notice (code feature_in_review while that permission is in Meta app review, reconsent_required once reconnecting grants it). list_platforms shows the state.
⟨420 unchanged words⟩ a commenter, public_reply = comment under the post. ai: window | public_reply | post_comment (post_comment = read the neighbor's post that triggered the flow and leave one comment; neighbor_post trigger only).","enum":["window","private_reply","public_reply","post_comment"],"type":"string"},"next":{ ⟨158 unchanged words⟩ story_reply","story_mention","referral","mention","neighbor_post","contact_created","tag_added","tag_removed","field_changed","schedule"],"type":"string"},"neighbors":{"description":"neighbor_post trigger (Naver Blog): mutual = only mutual neighbors (default), all = every neighbor in the feed.","enum":["mutual","all"],"type":"string"},"post": ⟨136 unchanged words⟩
⟨42 unchanged words⟩ draft. Fields you leave out keep their currentvalue;value (on a Naver Blog post, see media below); options you pass replace the whole options object ⟨394 unchanged words⟩ logNo) when you pass content, mediaIds oroptionsoptions.—Onlista post that goes only to Naver Blog, the media:idsreferencesagainin the new body are the post's media list: when you leave mediaIds out, media the new body does not place is removed from the post and the response note says how many. Pass mediaIds only for media tokeepadd at thephotos.end of the post without placing it in the body. If the body places more photos or videos than Naver takes (list_platforms), this returns 422 naver_media_over_limit with droppedIds and nothing changes. Naver Blog caps how many posts one ID ⟨230 unchanged words⟩
⟨89 unchanged words⟩ New media, in order. Replaces the current set. On a Naver Blog post the body's media: references already are the list, so leave this out unless you add media to the end of the post without placing it.","items":{"type":"string"}, ⟨236 unchanged words⟩
⟨258 unchanged words⟩ , \"any\", or \"next\".","type":"string"},"publicReply":{"description":"Optional public ⟨83 unchanged words⟩
⟨210 unchanged words⟩ through us. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨58 unchanged words⟩ no unfollow. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨27 unchanged words⟩ not zero. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨17 unchanged words⟩ open_post instead. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨92 unchanged words⟩ fake success. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨79 unchanged words⟩ out private. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨64 unchanged words⟩ get extension_outdated. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨30 unchanged words⟩ is normal. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨41 unchanged words⟩ that part. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨131 unchanged words⟩ post instead. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨74 unchanged words⟩ every call. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
⟨69 unchanged words⟩ and link. If the response carries `bridge`,follow`bridge.nextStep` says what is needed: `run_wake` meansrun `bridge.wake`Chromeforwith theuser's OSextensionifisyouclosed,havesoatellshelltheonpersontheir`bridge.userMessage`computer,withwaittheabout`bridge.wake`acommandminute,forcalltheirbridge_status,OSthenand retry once,otherwise tell theafteruserthey`bridge.userMessage`sayasChromeitisis;open; `ask_user_login` / `ask_user_update` mean tell theuserperson `bridge.userMessage`. Do not retry more than twice.
Beta.Searches YouTube for a keyword and returns the ⟨156 unchanged words⟩
{"properties":{"scope":{"anyOf":[{"enum":["all"],"type":"string"},{"items":{"type":"string"},"type":"array"}],"description":"Omit to list candidates, \"all\ ⟨58 unchanged words⟩
⟨124 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
⟨50 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
⟨45 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
StepUse1thisofonlyattachingwhenanyouimagecan HTTP PUT the file bytes yourself (a shell orvideo.code runtime). Web and mobile chat clients cannot, so do not call it there: for a file on the person's device use media_upload_link, for a public https address use media_from_url. Returns a media id and a one-time uploadUrl. ⟨77 unchanged words⟩
⟨178 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
⟨134 unchanged words⟩ they pick. Do not pick for them. If a Naver target stopped right after Save as draft (errorCode naver_draft_unknown), this returns 409 naver_draft_unknown and retries nothing; check list_naver_drafts and call again with force: true only if the draft is not there. If nothing failed you get nothing_to_retry. Only applies to ⟨57 unchanged words⟩
{"properties":{"force":{"description":"Retry even though a Naver target is naver_draft_unknown. Pass it only after list_naver_drafts shows the draft is not there.","type":"boolean"},"id":{"description":"A publish id ( ⟨91 unchanged words⟩
⟨141 unchanged words⟩ person has not turned this tool on forconnectors;connectors.tellTell themtoonceopenandthatdolink.not call it again until they say it is on.
⟨142 unchanged words⟩ "type":["string","null"]},"sourceVersion":{"description":"Only for a Naver post written in Naver's editor: the sourceVersion that came with the source you edited (from naver_source_required or get_post). It tells uplika the content was made from the post's current source.","type":"string"},"threadItems":{"description":"Replace the whole chain ⟨104 unchanged words⟩
⟨146 unchanged words⟩ the whole post (title, subheadings, body, and [사진: …] photoplacementslots for the person to fill); pass topicandtophotosputtothefillpost'sitsubjectin.in it. Returns 403 research_tool_disabled with enableUrl if the person ⟨14 unchanged words⟩
⟨13 unchanged words⟩ :"string"},"type":"array"},"photos":{"description":"One line per photo saying what it shows, in order. draftPrompt places them; a placeholder otherwise.","items":{"type":"string"},"type":"array"},"seedText":{"description":"What the person asked ⟨19 unchanged words⟩ is about, one line. Goes into draftPrompt; without it the prompt tells the writer to pick aplaceholdersubjectotherwise.that fits the main keyword.","type":"string"},"workspaceId":{ ⟨45 unchanged words⟩
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Create an automation ⟨4 unchanged words⟩
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Create a comment-to-DM automation (draft)"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Delete a post"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Delete a reply"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Look up a channel's body syntax"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Diagnose a Naver ⟨4 unchanged words⟩
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Stop an automation"}
{"destructiveHint":true,"openWorldHint":false,"readOnlyHint":false,"title":"Turn an automation live"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Expand seed keywords through Naver autocomplete"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Follow a blog"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Read one automation"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Read a contact"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Get post metrics"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Get a keyword's measurement history"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Get one post"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Check what a ⟨4 unchanged words⟩
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Get remaining publishing quota"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Hide a reply"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Like a post or a reply"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List connected accounts"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List an automation's runs"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List automation templates"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List automations"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"List posts on the channel"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List inbox conversations"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List Threads mentions"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"List the drafts ⟨4 unchanged words⟩
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List saved keyword research"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List platform limits"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"List posts published here"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"List replies to a post"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Finish a media upload"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Add media from a public URL"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Start a direct media upload"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Create an upload link"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Check upload status"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Preview the Naver ⟨4 unchanged words⟩
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Open a post ⟨4 unchanged words⟩
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Publish a post"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Publish a Naver ⟨5 unchanged words⟩
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Send a scheduled ⟨4 unchanged words⟩
ReplaceEdit anautomation'sautomation (replace its flow)
ReplaceEdit anautomation'sautomation: change its triggers, messages or steps by replacing its wholedocument.documentPass(there is no partial update). Read it first with get_automation and pass the version yougot from get_automation;got; a stale version is refused with version_conflict so ⟨60 unchanged words⟩
{"destructiveHint":true,"openWorldHint":false,"readOnlyHint":false,"title":"ReplaceEdit anautomation'sautomation (replace its flow)"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Read a conversation"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":false,"title":"Re-read a channel's metadata"}
⟨21 unchanged words⟩ from list_replies to nest a reply under thatreply.Likereply. Like publish, this returns before the reply islive.liveCallunless you pass wait: true, which holds the response until it is out (up to 10 seconds). Otherwise call get_post with the returned id to see the ⟨58 unchanged words⟩
⟨89 unchanged words⟩ refuse it.","type":"boolean"},"wait":{"description":"Hold the response until the reply is really out, up to 10 seconds, so you can say it is live. If it is still going you get the usual publishing response; poll get_post. Defaults to false.","type":"boolean"},"workspaceId":{"description":"Which workspace this is ⟨41 unchanged words⟩
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Reply to a post"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Measure and judge ⟨4 unchanged words⟩
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Retry a failed post"}
{"destructiveHint":false,"openWorldHint":true,"readOnlyHint":true,"title":"Search YouTube for benchmark videos"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":true,"title":"Select channels to post to"}
{"destructiveHint":true,"openWorldHint":true,"readOnlyHint":false,"title":"Send a message in a conversation"}
{"destructiveHint":false,"openWorldHint":false,"readOnlyHint":false,"title":"Mark media as AI-made"}
Edit a scheduled, draft ordraftlive post
Change a scheduled or draft post before it goes out, or edit a post that is already live on a channel that supports editing (list_platforms features: today Naver Blog, YouTube and some Facebook posts). Fields: content, mediaIds, accountIds, options, scheduledAt or draft. Fields you leave out keep their current value; options you pass replace the whole options object. scheduledAt moves the send time (same rules as publish), null turns it into a draft, and draft: true does the same. A thread (chain) can be changed too while it is scheduled or a draft: content replaces the first item and keeps the later items; threadItems replaces the whole chain (one item collapses it to a single post); passing threadItems to a single post turns it into a chain. Pass id of the first item — later items return 409 thread_piece with rootId. Posts that already went out return post_not_editable on channels that cannot edit a live post. A published Naver post is rewritten in place (same URL, same logNo) when you pass content, mediaIds or options — list the media ids again to keep the photos. Naver Blog caps how many posts one ID publishes; ⟨142 unchanged words⟩ and fixes it; the URL stays the same.options, scheduledAt or draft. Fields you leave out keep their current value; options you pass replace the whole options object. scheduledAt moves the send time (same rules as publish), null turns it into a draft, and draft: true does the same. A thread (chain) can be changed too while it is scheduled or a draft: content replaces the first item and keeps the later items; threadItems replaces the whole chain (one item collapses it to a single post); passing threadItems to a single post turns it into a chain. Pass id of the first item — later items return 409 thread_piece with rootId. Posts that already went out return post_not_editable, except Naver Blog: a published Naver post is rewritten in place (same URL, same logNo) when you pass content, mediaIds or options — list the media ids again to keep the photos.A Naver draft (a target whose externalId ⟨44 unchanged words⟩
{"destructiveHint":falsetrue,"openWorldHint":true,"readOnlyHint":false,"title":"Edit a scheduled, draft ordraftlive post"}
⟨21 unchanged words⟩ report them yet — null is not zero. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨11 unchanged words⟩ a post link you probably want open_post instead. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨86 unchanged words⟩ back as naver_not_allowed rather than a fake success. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨73 unchanged words⟩ Posting audit and every post goes out private. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
Naver Blog only. Lists the drafts (임시저장temp-saved posts) sitting in the blog's draft box: logNo ⟨43 unchanged words⟩ in the person's Chrome; otherwise you get extension_outdated. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨24 unchanged words⟩ the replies metric in get_insights, which is normal. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨13 unchanged words⟩ changed with AI. Naver Blog photos get itsAI usage label (AI 활용AI-use설정);label; Instagram, YouTube and TikTok get their AI ⟨93 unchanged words⟩
⟨13 unchanged words⟩ changed with AI. Naver Blog photos get itsAI usage label (AI 활용AI-use설정);label; Instagram, YouTube and TikTok get their AI ⟨137 unchanged words⟩
⟨35 unchanged words⟩ null if the platform refused just that part. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨1582 unchanged words⟩ "description":"Set by publish_naver_draft: the Naverdrafttemp-saved(임시저장)draft to publish as it is. The body is ⟨1312 unchanged words⟩
⟨125 unchanged words⟩ in Naver, use update_post on that post instead. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨68 unchanged words⟩ because their metadata is live on every call. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
⟨43 unchanged words⟩ id to see the final status and link. If the response carries `bridge`, follow `bridge.nextStep`: `run_wake` means run `bridge.wake` for the user's OS if you have a shell on their computer, wait about a minute, call bridge_status, then retry once, otherwise tell the user `bridge.userMessage` as it is; `ask_user_login` / `ask_user_update` mean tell the user `bridge.userMessage`. Do not retry more than twice.
Diagnose a Naver blog's titles for search — Naver Blog only. Reads a blog's public RSS feed (its latest posts, at most 50) and says whether the titles are written for search: the share of titles carrying a search intent word (price, how to, review), how many start with a date or episode label, posts per month, the categories, and the words the blog repeats in titles (seedCandidates: candidates to research, not proven keywords). summary.oldest and summary.newest say which dates it read, so a quiet blog's 50 posts are not everything; summary.capped is true at 50. flags and thresholds carry the judgement (lowIntent when the intent share i…
Expand seed keywords through Naver autocomplete — Naver Blog only. Expands seed keywords one level through Naver autocomplete on the server and returns every word found with the seed it came from (found[].from is seed or L1:<seed>). These are candidates to measure with research_naver_keywords, not proven keywords. Up to 10 seeds. Cached seven days; a call with at least one uncached seed spends one of 20 expansions per person per day. Server-side autocomplete is off by default: when the server answers naver_autocomplete_unavailable, pass your own keyword list to research_naver_keywords instead. naver_autocomplete_paused and naver_autocomplete…
Get a keyword's measurement history — The measurement history of one keyword: one point per day it was actually measured, newest first, with search volume, document count and posts per month. Shows whether a keyword is rising or cooling. An empty list means nobody has measured it here yet. Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors; tell them to open that link.
List saved keyword research — Lists the person's saved keyword research (from research_naver_keywords or the dashboard), newest first: when, what they typed, the first set's main and sub keywords, and every set with its prompt. Reports belong to the person, not a workspace. Returns 403 research_tool_disabled with enableUrl if the person has not turned this tool on for connectors; tell them to open that link.
Measure and judge Naver keywords into sets — Naver Blog only. Measures keywords (monthly searches from Search Ad, blog document count and posts per month from API HUB), judges each one (best, possible, hard, wall, hot, saturated, phantom and so on, with why), and groups them into sets for one post: a main keyword plus two to five subs with the same search intent. Every set carries prompt, a ready-to-paste Korean brief for writing the skeleton of that post. Up to 60 keywords. Measurements are cached seven days across users; new ones run against a time budget, so partial: true with unmeasured[] means the budget ran out and calling again w…
Search YouTube for benchmark videos — Beta. Searches YouTube for a keyword and returns the top videos with views, subscribers, the views-to-subscribers ratio (above 1 means the title and topic pulled more people than the channel has), Shorts or long-form (60 seconds or less counts as a Short), duration and publish date. order is viewCount, date or relevance; period 7d, 1m, 3m, 6m or 1y; format all, shorts or long; max 1-50 (default 25). The same search is cached 24 hours and does not count; a new one spends one of the person's daily searches (quota in the response) and shared YouTube Data API units (youtube_quota_exhausted when t…
before
{"properties":{"accountIds":{"description":"Account ids from select_channels","items":{"type":"string"},"type":"array"},"automation":{"description":"Attach an automation to this post in the same call. template defaults to comment_to_dm; params are that template's params (see list_automation_templates) minus post, which is this post. Created as a draft unless enabled: true. Targets on channels without comments or DMs (YouTube, Telegram, Naver Blog, Bluesky, TikTok) are skipped and named in the response.","properties":{"enabled":{"type":"boolean"},"name":{"type":"string"},"params":{"type":"object"},"template":{"type":"string"}},"type":"object"},"batchId":{"description":"Optional tag (letters, digits, - or _) to group posts made together, for example the same text sent to two workspaces. Pass the same value on each call.","type":"string"},"content":{"description":"Post text","type":"string"},"draft":{"description":"true saves the post as a draft on uplika without sending anything. Use it when the person wants to review before it goes out. They finish it in the dashboard, or you call update_post and publish_now.","type":"boolean"},"mediaIds":{"description":"Media ids from media_presign (confirmed with media_complete), media_from_url or media_upload_link. On Naver Blog you can leave out ids the body already points at with media:<id>; we pick those up from the text.","items":{"type":"string"},"type":"array"},"options":{"description":"Per-channel settings, keyed by channel id. Only the channels you are posting to need an entry. Every channel takes content to override the shared text for that channel alone.","properties":{"bluesky":{"description":"Bluesky settings. Optional, but langs is worth setting on every post.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"langs":{"description":"The language of the text, 1-3 BCP-47 codes like [\"ko\"] or [\"en\", \"ko\"]. Feed generators filter on this, so without it the post never appears in language-scoped feeds. Bluesky has no post editing, so this cannot be fixed after publishing. Set it to the language the text is actually written in.","items":{"description":"One BCP-47 code.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"maxItems":3,"type":"array"}},"type":"object"},"facebook":{"description":"Facebook Page settings. Optional: plain text publishes without it.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. A Facebook story is rejected if you send any text: publish the story with no content, and put the words in a feed post instead. A story also takes exactly one image or video and no first comment.","enum":["feed","story","reel"],"type":"string"},"firstComment":{"description":"A comment we post right after publishing. Not on stories, and not on scheduled posts. If it fails the post still goes up, with a warning.","type":"string"},"link":{"description":"Makes a link post with a preview card. Cannot be combined with media.","type":"string"},"scheduledPublishTime":{"description":"ISO 8601 time to publish a feed post later, 10 minutes to 28 days from now. Feed posts only, not reels or stories. Cannot be combined with firstComment (a scheduled post is not live yet, so nothing to comment on). This is Facebook's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"title":{"description":"Reel title, separate from the caption.","type":"string"}},"type":"object"},"instagram":{"description":"Instagram settings. Optional: a plain post with media works without it.","properties":{"collaborators":{"description":"Up to 3 public professional accounts to invite as collaborators. They appear only after accepting. Not on stories.","items":{"description":"A public professional account's username, without @.","type":"string"},"maxItems":3,"type":"array"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. Stories show no caption and take one image or video.","enum":["feed","story","reel"],"type":"string"},"coverMediaId":{"description":"Media id of a JPEG up to 8MB to use as the reel cover. Reels only.","type":"string"},"firstComment":{"description":"A comment we post right after publishing, often used for hashtags. Not on stories. If it fails the post still goes up, with a warning.","type":"string"},"isAiGenerated":{"description":"Set true to label the media as AI-generated on Instagram.","type":"boolean"},"shareToFeed":{"description":"Reels only. false keeps the reel out of the main feed.","type":"boolean"},"thumbOffsetMs":{"description":"Reel cover frame in milliseconds. Ignored when coverMediaId is set.","minimum":0,"type":"number"},"userTags":{"description":"Tag accounts on the media. Photos need x and y between 0 and 1; videos take the username alone. mediaIndex picks the carousel slide.","items":{"description":"One tagged account.","properties":{"mediaIndex":{"description":"Which carousel slide, from 0.","type":"number"},"username":{"description":"Username without @.","type":"string"},"x":{"description":"0 to 1 from the left. Photos only.","type":"number"},"y":{"description":"0 to 1 from the top. Photos only.","type":"number"}},"required":["username"],"type":"object"},"type":"array"}},"type":"object"},"naver_blog":{"description":"Naver Blog settings. Required when a target is a Naver Blog account. Naver Blog has no official API: the post is written by the user's browser extension, so it goes out only while that browser is open. list_accounts tells you whether the extension is online; if it is offline the post queues for up to 7 days and you should say so. The body is Markdown, plus Naver-only markup for things Markdown has no word for (highlighting, styled tables, collages, maps, events): call describe_grammar with platform naver_blog for the syntax and its traps. Reference uploaded media inline with ``; ids you reference this way do not have to repeat in mediaIds, and media you pass but never reference goes at the end. When other channels are in the same post, put the Markdown in options.naver_blog.content so the shared content stays plain text.","properties":{"alignCenter":{"description":"Center every paragraph and image.","type":"boolean"},"category":{"description":"The category name instead of its id, exactly as it appears in naverBlog.categories from list_accounts. If the blog does not have it you get naver_category_unknown with the list to pick from; if the person just created it, call refresh_account first.","type":"string"},"categoryId":{"description":"The id of a category on the connected blog. list_accounts returns them under naverBlog.categories. Give this or category (the name); there is no default, and without one Naver files the post under the wrong board.","type":"string"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content. On Naver Blog this is where the Markdown body goes when the other channels get plain text.","type":"string"},"draftOnly":{"description":"Save as a draft in Naver instead of publishing. The user finishes it by hand, or you publish it later with publish_naver_draft. Different from draft on the post, which keeps the whole post on uplika.","type":"boolean"},"fontFamily":{"description":"Body font, one of nanumgothic, nanummyeongjo, nanumbarungothic, nanumsquare, maruburi. Omit for the editor default.","enum":["nanumgothic","nanummyeongjo","nanumbarungothic","nanumsquare","maruburi"],"type":"string"},"fontSize":{"description":"Body font size in px, one of the editor's sizes (11-38). Omit for the editor default.","enum":["11","13","15","16","19","24","28","30","34","38"],"maximum":34,"minimum":10,"type":"number"},"form":{"description":"Form preset for the template layout (default photo-story). GET /v1/naver/forms lists the presets; naver_layout with forms: \"all\" shows them side by side.","enum":["bubble-info","divider-tutorial","photo-review","place-list","news-event","table-explain","mood-review","photo-story"],"type":"string"},"layout":{"description":"template (default) reshapes the body into the blog's house form before it goes out: #/## titles become underlined quote headings, a thin rule sits between sections, and photos you did not place with media: references go one per section (leftovers pair up at the end). Text is never changed. Call naver_layout first to see the result. Use as-is when the person dictated the structure or you placed everything yourself.","enum":["template","as-is"],"type":"string"},"openType":{"description":"Who can see it. public (default), neighbor, mutual (mutual neighbors only), or private.","enum":["public","neighbor","mutual","private"],"type":"string"},"scheduledAt":{"description":"Schedule the post inside Naver (ISO 8601, at least 10 minutes from now). The extension sets it in Naver's publish dialog. For scheduling across channels use scheduledAt on the post instead.","type":"string"},"seoKeywords":{"description":"The search keywords this post targets, from keyword research (POST /v1/naver/keywords/judge): main is the one keyword the title leads with, subs (2-5) become the section titles one each. Not used when publishing; stored with the post and read by the SEO check.","properties":{"main":{"description":"The main keyword, verbatim in the title and the first line.","maxLength":100,"type":"string"},"measuredAt":{"description":"ISO 8601 date the numbers were measured.","type":"string"},"subs":{"description":"Sub keywords, one per section title.","items":{"description":"One sub keyword.","maxLength":100,"type":"string"},"maxItems":5,"type":"array"}},"type":"object"},"tags":{"description":"Up to 30 tags without the # sign and without spaces.","items":{"description":"One tag without # and without spaces.","type":"string"},"maxItems":30,"type":"array"},"tempLogNo":{"description":"Set by publish_naver_draft: the Naver draft (임시저장) to publish as it is. The body is ignored; the extension loads that draft in the editor and presses publish.","type":"string"},"title":{"description":"Required. The post title, up to 100 characters.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"},"telegram":{"description":"Telegram has no channel-specific settings. Note the caption limit: with media attached the text is capped at 1,024 characters instead of 4,096.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"}},"type":"object"},"threads":{"description":"Threads settings. Optional.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"topicTag":{"description":"One topic to tag the post with, like a category. Threads takes exactly one and rejects periods and ampersands in it. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","maxLength":64,"type":"string"}},"type":"object"},"tiktok":{"description":"TikTok settings. privacyLevel is REQUIRED for a normal post and has no default on purpose: TikTok's rules say the person must choose visibility deliberately, so we ask for it instead of guessing. Call get_publish_options first to see which values this account may use right now, whether it can post at all, and its video length limit. TikTok has no text-only posts, cannot mix a video and photos, and has no API for deleting a post or reading comments. While this app is awaiting TikTok's Content Posting audit a direct post can only be SELF_ONLY and comes back with no link and no metrics; if the person wants it public in the meantime, use postMode draft instead.","properties":{"brandContentToggle":{"description":"Declare paid partnership content (\"Branded Content\"). TikTok does not allow branded content to be private, so this cannot be combined with privacyLevel SELF_ONLY.","type":"boolean"},"brandOrganicToggle":{"description":"Declare that the post promotes the creator's own brand (\"Your Brand\"). Leave it out rather than sending false: not declaring and declaring 'no' are different statements.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"disableComment":{"description":"Turn comments off for this post. Cannot be set to false if the creator disabled comments account-wide.","type":"boolean"},"disableDuet":{"description":"Turn duets off for this post. Video posts only.","type":"boolean"},"disableStitch":{"description":"Turn stitches off for this post. Video posts only.","type":"boolean"},"isAigc":{"description":"Declare that the content was generated by AI. Worth setting when you made the video or images.","type":"boolean"},"postMode":{"description":"direct (default) posts to the profile. draft sends it to the creator's TikTok inbox; they open the notification and finish it in the app, choosing the visibility themselves. While this app awaits the Content Posting audit this is the only way to get something out publicly: a direct post can only be SELF_ONLY, but on this path we send no visibility at all. The trade is that it is not on the profile until the person finishes it, so there is no link and no metrics in the meantime.","enum":["direct","draft"],"type":"string"},"privacyLevel":{"description":"Required unless postMode is draft. One of the values that get_publish_options returns for this account, e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. The list differs per account, so do not hard-code it.","type":"string"},"title":{"description":"Required for a photo post, up to 90 characters. A video post has no title field at all and passing one is rejected; a video's text comes from content.","maxLength":90,"type":"string"},"videoCoverTimestampMs":{"description":"Which frame to use as the cover, in milliseconds into the video. TikTok does not accept a cover image file, only a timestamp.","minimum":0,"type":"number"}},"type":"object"},"youtube":{"description":"Required when a YouTube account is in accountIds. The post content becomes the video description.","properties":{"categoryId":{"description":"YouTube category id. Defaults to 22 (People & Blogs). Leave it out unless the person named a category.","type":"string"},"containsSyntheticMedia":{"description":"Set true when the video contains realistic altered or synthetic content, including AI generated footage of real-looking people, places or events.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"madeForKids":{"description":"Whether this video is directed at children. This is a legal declaration about someone else's channel. Leave it out unless the person tells you, and the channel's own default applies.","type":"boolean"},"privacyStatus":{"description":"Defaults to private. We pass this through as you set it. If YouTube applies something else, the result carries a warning saying what it actually applied.","enum":["public","unlisted","private"],"type":"string"},"publishAt":{"description":"ISO 8601 time to make the video public. Only works with privacyStatus private, and only on a video that was never public. This is YouTube's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"tags":{"description":"Search tags. 500 characters across all of them; a tag with a space costs two extra for the quotes YouTube adds.","items":{"description":"One search tag.","type":"string"},"type":"array"},"thumbnailMediaId":{"description":"Media id of a JPEG or PNG up to 2MB to use as the thumbnail. Long-form only: YouTube does not take custom thumbnails on Shorts, and a vertical video of three minutes or less becomes a Short. The channel also has to be verified before YouTube accepts one.","type":"string"},"title":{"description":"Video title, up to 100 characters. Required. Cannot contain < or >.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"}},"type":"object"},"scheduledAt":{"description":"ISO 8601 time with a timezone offset (2026-09-20T09:00:00+09:00) to send the post at, on every channel. 10 minutes to 365 days from now. We keep the post as scheduled until then; the person can still change or cancel it.","type":"string"},"threadItems":{"description":"Split a long post into a chain instead of calling publish and then reply. The first item is the root and the rest become replies under it, in order. We handle the ordering and the waiting. Each item obeys the character limit on its own. Pass either content or threadItems, not both. If a later item fails, the ones already up stay up and the response tells you where to resume.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"topicTag":{"description":"One topic to tag the post with, like a category. Only some channels take one, and those reject periods and ampersands in it. If any channel in accountIds does not take topics the whole call is refused, so publish to it separately. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","type":"string"},"wait":{"description":"Hold the response until the post is really out. Text waits up to 10 seconds, posts with media up to 90 seconds. If it is still going after that you get the usual publishing response and should poll get_post. Defaults to false. Ignored with scheduledAt or draft.","type":"boolean"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["accountIds"],"type":"object"}after
{"properties":{"accountIds":{"description":"Account ids from select_channels","items":{"type":"string"},"type":"array"},"automation":{"description":"Attach an automation to this post in the same call. template defaults to comment_to_dm; params are that template's params (see list_automation_templates) minus post, which is this post. Created as a draft unless enabled: true. Targets on channels without comments or DMs (YouTube, Telegram, Naver Blog, Bluesky, TikTok) are skipped and named in the response.","properties":{"enabled":{"type":"boolean"},"name":{"type":"string"},"params":{"type":"object"},"template":{"type":"string"}},"type":"object"},"batchId":{"description":"Optional tag (letters, digits, - or _) to group posts made together, for example the same text sent to two workspaces. Pass the same value on each call.","type":"string"},"content":{"description":"Post text","type":"string"},"draft":{"description":"true saves the post as a draft on uplika without sending anything. Use it when the person wants to review before it goes out. They finish it in the dashboard, or you call update_post and publish_now.","type":"boolean"},"mediaIds":{"description":"Media ids from media_presign (confirmed with media_complete), media_from_url or media_upload_link. On Naver Blog you can leave out ids the body already points at with media:<id>; we pick those up from the text.","items":{"type":"string"},"type":"array"},"options":{"description":"Per-channel settings, keyed by channel id. Only the channels you are posting to need an entry. Every channel takes content to override the shared text for that channel alone.","properties":{"bluesky":{"description":"Bluesky settings. Optional, but langs is worth setting on every post.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"langs":{"description":"The language of the text, 1-3 BCP-47 codes like [\"ko\"] or [\"en\", \"ko\"]. Feed generators filter on this, so without it the post never appears in language-scoped feeds. Bluesky has no post editing, so this cannot be fixed after publishing. Set it to the language the text is actually written in.","items":{"description":"One BCP-47 code.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"maxItems":3,"type":"array"}},"type":"object"},"facebook":{"description":"Facebook Page settings. Optional: plain text publishes without it.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. A Facebook story is rejected if you send any text: publish the story with no content, and put the words in a feed post instead. A story also takes exactly one image or video and no first comment.","enum":["feed","story","reel"],"type":"string"},"firstComment":{"description":"A comment we post right after publishing. Not on stories, and not on scheduled posts. If it fails the post still goes up, with a warning.","type":"string"},"link":{"description":"Makes a link post with a preview card. Cannot be combined with media.","type":"string"},"scheduledPublishTime":{"description":"ISO 8601 time to publish a feed post later, 10 minutes to 28 days from now. Feed posts only, not reels or stories. Cannot be combined with firstComment (a scheduled post is not live yet, so nothing to comment on). This is Facebook's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"title":{"description":"Reel title, separate from the caption.","type":"string"}},"type":"object"},"instagram":{"description":"Instagram settings. Optional: a plain post with media works without it.","properties":{"collaborators":{"description":"Up to 3 public professional accounts to invite as collaborators. They appear only after accepting. Not on stories.","items":{"description":"A public professional account's username, without @.","type":"string"},"maxItems":3,"type":"array"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. Stories show no caption and take one image or video.","enum":["feed","story","reel"],"type":"string"},"coverMediaId":{"description":"Media id of a JPEG up to 8MB to use as the reel cover. Reels only.","type":"string"},"firstComment":{"description":"A comment we post right after publishing, often used for hashtags. Not on stories. If it fails the post still goes up, with a warning.","type":"string"},"isAiGenerated":{"description":"Set true to label the media as AI-generated on Instagram. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"shareToFeed":{"description":"Reels only. false keeps the reel out of the main feed.","type":"boolean"},"thumbOffsetMs":{"description":"Reel cover frame in milliseconds. Ignored when coverMediaId is set.","minimum":0,"type":"number"},"userTags":{"description":"Tag accounts on the media. Photos need x and y between 0 and 1; videos take the username alone. mediaIndex picks the carousel slide.","items":{"description":"One tagged account.","properties":{"mediaIndex":{"description":"Which carousel slide, from 0.","type":"number"},"username":{"description":"Username without @.","type":"string"},"x":{"description":"0 to 1 from the left. Photos only.","type":"number"},"y":{"description":"0 to 1 from the top. Photos only.","type":"number"}},"required":["username"],"type":"object"},"type":"array"}},"type":"object"},"naver_blog":{"description":"Naver Blog settings. Required when a target is a Naver Blog account. Naver Blog has no official API: the post is written by the user's browser extension, so it goes out only while that browser is open. list_accounts tells you whether the extension is online; if it is offline the post queues for up to 7 days and you should say so. The body is Markdown, plus Naver-only markup for things Markdown has no word for (highlighting, styled tables, collages, maps, events): call describe_grammar with platform naver_blog for the syntax and its traps. Reference uploaded media inline with ``; ids you reference this way do not have to repeat in mediaIds, and media you pass but never reference goes at the end. When other channels are in the same post, put the Markdown in options.naver_blog.content so the shared content stays plain text.","properties":{"alignCenter":{"description":"Center every paragraph and image.","type":"boolean"},"category":{"description":"The category name instead of its id, exactly as it appears in naverBlog.categories from list_accounts. If the blog does not have it you get naver_category_unknown with the list to pick from; if the person just created it, call refresh_account first.","type":"string"},"categoryId":{"description":"The id of a category on the connected blog. list_accounts returns them under naverBlog.categories. Give this or category (the name); there is no default, and without one Naver files the post under the wrong board.","type":"string"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content. On Naver Blog this is where the Markdown body goes when the other channels get plain text.","type":"string"},"draftOnly":{"description":"Save as a draft in Naver instead of publishing. The user finishes it by hand, or you publish it later with publish_naver_draft. Different from draft on the post, which keeps the whole post on uplika.","type":"boolean"},"fontFamily":{"description":"Body font, one of nanumgothic, nanummyeongjo, nanumbarungothic, nanumsquare, maruburi. Omit for the editor default.","enum":["nanumgothic","nanummyeongjo","nanumbarungothic","nanumsquare","maruburi"],"type":"string"},"fontSize":{"description":"Body font size in px, one of the editor's sizes (11-38). Omit for the editor default.","enum":["11","13","15","16","19","24","28","30","34","38"],"maximum":34,"minimum":10,"type":"number"},"form":{"description":"Form preset for the template layout (default photo-story). GET /v1/naver/forms lists the presets; naver_layout with forms: \"all\" shows them side by side.","enum":["bubble-info","divider-tutorial","photo-review","place-list","news-event","table-explain","mood-review","photo-story"],"type":"string"},"layout":{"description":"template (default) reshapes the body into the blog's house form before it goes out: #/## titles become underlined quote headings, a thin rule sits between sections, and photos you did not place with media: references go one per section (leftovers pair up at the end). Text is never changed. Call naver_layout first to see the result. Use as-is when the person dictated the structure or you placed everything yourself.","enum":["template","as-is"],"type":"string"},"openType":{"description":"Who can see it. public (default), neighbor, mutual (mutual neighbors only), or private.","enum":["public","neighbor","mutual","private"],"type":"string"},"scheduledAt":{"description":"Schedule the post inside Naver (ISO 8601, at least 10 minutes from now). The extension sets it in Naver's publish dialog. For scheduling across channels use scheduledAt on the post instead.","type":"string"},"seoKeywords":{"description":"The search keywords this post targets, from keyword research (POST /v1/naver/keywords/judge): main is the one keyword the title leads with, subs (2-5) become the section titles one each. Not used when publishing; stored with the post and read by the SEO check.","properties":{"main":{"description":"The main keyword, verbatim in the title and the first line.","maxLength":100,"type":"string"},"measuredAt":{"description":"ISO 8601 date the numbers were measured.","type":"string"},"subs":{"description":"Sub keywords, one per section title.","items":{"description":"One sub keyword.","maxLength":100,"type":"string"},"maxItems":5,"type":"array"}},"type":"object"},"tags":{"description":"Up to 30 tags without the # sign and without spaces.","items":{"description":"One tag without # and without spaces.","type":"string"},"maxItems":30,"type":"array"},"tempLogNo":{"description":"Set by publish_naver_draft: the Naver draft (임시저장) to publish as it is. The body is ignored; the extension loads that draft in the editor and presses publish.","type":"string"},"title":{"description":"Required. The post title, up to 100 characters.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"},"telegram":{"description":"Telegram has no channel-specific settings. Note the caption limit: with media attached the text is capped at 1,024 characters instead of 4,096.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"}},"type":"object"},"threads":{"description":"Threads settings. Optional.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"topicTag":{"description":"One topic to tag the post with, like a category. Threads takes exactly one and rejects periods and ampersands in it. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","maxLength":64,"type":"string"}},"type":"object"},"tiktok":{"description":"TikTok settings. privacyLevel is REQUIRED for a normal post and has no default on purpose: TikTok's rules say the person must choose visibility deliberately, so we ask for it instead of guessing. Call get_publish_options first to see which values this account may use right now, whether it can post at all, and its video length limit. TikTok has no text-only posts, cannot mix a video and photos, and has no API for deleting a post or reading comments. While this app is awaiting TikTok's Content Posting audit a direct post can only be SELF_ONLY and comes back with no link and no metrics; if the person wants it public in the meantime, use postMode draft instead.","properties":{"brandContentToggle":{"description":"Declare paid partnership content (\"Branded Content\"). TikTok does not allow branded content to be private, so this cannot be combined with privacyLevel SELF_ONLY.","type":"boolean"},"brandOrganicToggle":{"description":"Declare that the post promotes the creator's own brand (\"Your Brand\"). Leave it out rather than sending false: not declaring and declaring 'no' are different statements.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"disableComment":{"description":"Turn comments off for this post. Cannot be set to false if the creator disabled comments account-wide.","type":"boolean"},"disableDuet":{"description":"Turn duets off for this post. Video posts only.","type":"boolean"},"disableStitch":{"description":"Turn stitches off for this post. Video posts only.","type":"boolean"},"isAigc":{"description":"Declare that the content was generated by AI. Worth setting when you made the video or images. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"postMode":{"description":"direct (default) posts to the profile. draft sends it to the creator's TikTok inbox; they open the notification and finish it in the app, choosing the visibility themselves. While this app awaits the Content Posting audit this is the only way to get something out publicly: a direct post can only be SELF_ONLY, but on this path we send no visibility at all. The trade is that it is not on the profile until the person finishes it, so there is no link and no metrics in the meantime.","enum":["direct","draft"],"type":"string"},"privacyLevel":{"description":"Required unless postMode is draft. One of the values that get_publish_options returns for this account, e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. The list differs per account, so do not hard-code it.","type":"string"},"title":{"description":"Required for a photo post, up to 90 characters. A video post has no title field at all and passing one is rejected; a video's text comes from content.","maxLength":90,"type":"string"},"videoCoverTimestampMs":{"description":"Which frame to use as the cover, in milliseconds into the video. TikTok does not accept a cover image file, only a timestamp.","minimum":0,"type":"number"}},"type":"object"},"youtube":{"description":"Required when a YouTube account is in accountIds. The post content becomes the video description.","properties":{"categoryId":{"description":"YouTube category id. Defaults to 22 (People & Blogs). Leave it out unless the person named a category.","type":"string"},"containsSyntheticMedia":{"description":"Set true when the video contains realistic altered or synthetic content, including AI generated footage of real-looking people, places or events. An attached media marked aiGenerated turns this on automatically unless you set it to false.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"madeForKids":{"description":"Whether this video is directed at children. This is a legal declaration about someone else's channel. Leave it out unless the person tells you, and the channel's own default applies.","type":"boolean"},"privacyStatus":{"description":"Defaults to private. We pass this through as you set it. If YouTube applies something else, the result carries a warning saying what it actually applied.","enum":["public","unlisted","private"],"type":"string"},"publishAt":{"description":"ISO 8601 time to make the video public. Only works with privacyStatus private, and only on a video that was never public. This is YouTube's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"tags":{"description":"Search tags. 500 characters across all of them; a tag with a space costs two extra for the quotes YouTube adds.","items":{"description":"One search tag.","type":"string"},"type":"array"},"thumbnailMediaId":{"description":"Media id of a JPEG or PNG up to 2MB to use as the thumbnail. Long-form only: YouTube does not take custom thumbnails on Shorts, and a vertical video of three minutes or less becomes a Short. The channel also has to be verified before YouTube accepts one.","type":"string"},"title":{"description":"Video title, up to 100 characters. Required. Cannot contain < or >.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"}},"type":"object"},"scheduledAt":{"description":"ISO 8601 time with a timezone offset (2026-09-20T09:00:00+09:00) to send the post at, on every channel. 10 minutes to 365 days from now. We keep the post as scheduled until then; the person can still change or cancel it.","type":"string"},"threadItems":{"description":"Split a long post into a chain instead of calling publish and then reply. The first item is the root and the rest become replies under it, in order. We handle the ordering and the waiting. Each item obeys the character limit on its own. Pass either content or threadItems, not both. If a later item fails, the ones already up stay up and the response tells you where to resume.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"topicTag":{"description":"One topic to tag the post with, like a category. Only some channels take one, and those reject periods and ampersands in it. If any channel in accountIds does not take topics the whole call is refused, so publish to it separately. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","type":"string"},"wait":{"description":"Hold the response until the post is really out. Text waits up to 10 seconds, posts with media up to 90 seconds. If it is still going after that you get the usual publishing response and should poll get_post. Defaults to false. Ignored with scheduledAt or draft.","type":"boolean"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["accountIds"],"type":"object"}Mark media as AI-made — Mark or unmark an uploaded image or video as AI-made (aiGenerated). Applies to posts published or edited after this call; posts already out do not change (use update_post to rewrite a Naver Blog post). Every copy of the same file in your workspaces follows.
⟨1179 unchanged words⟩ instead of publishing. The user finishes it byhand.hand, or you publish it later with publish_naver_draft. Different from draft on the post, which keeps ⟨358 unchanged words⟩ string"},"maxItems":30,"type":"array"},"tempLogNo":{"description":"Set by publish_naver_draft: the Naver draft (임시저장) to publish as it is. The body is ignored; the extension loads that draft in the editor and presses publish.","type":"string"},"title":{"description":"Required. The ⟨1264 unchanged words⟩
⟨369 unchanged words⟩ ,"assign","hide_reply","approve_reply","follow","like_comment"],"type":"string"},"userId":{ ⟨364 unchanged words⟩
⟨208 unchanged words⟩ draft: true does the same. A threadonly(chain)takescanschedulebechanges.changed too while it is scheduled or a draft: content replaces the first item and keeps the later items; threadItems replaces the whole chain (one item collapses it to a single post); passing threadItems to a single post turns it into a chain. Pass id of the first item — later items return 409 thread_piece with rootId. Posts that already went out return post_not_editable, except ⟨23 unchanged words⟩ the media ids again to keep the photos. A Naver draft (a target whose externalId starts with draft:, from options.naver_blog.draftOnly) is not on the blog: update_post publishes uplika's copy as a new post and, with extension 0.7.2 or later, removes the old draft from Naver's draft box. To publish the draft exactly as it is in Naver, call publish_naver_draft.
⟨142 unchanged words⟩ "type":["string","null"]},"threadItems":{"description":"Replace the whole chain: [{ content, mediaIds? }], first item is the root. Only while the post is scheduled or a draft, and only for Threads and Bluesky. Leave it out to keep the current items (content then edits just the first one). Exclusive with content.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"workspaceId":{"description":"Which workspace this is ⟨40 unchanged words⟩
List the drafts saved in Naver Blog — Naver Blog only. Lists the drafts (임시저장) sitting in the blog's draft box: logNo, title and the last-saved time, newest first. A post you published with options.naver_blog.draftOnly is one of them, and so is anything the person saved by hand in the Naver editor. Pass a logNo from here to publish_naver_draft. Needs the extension 0.7.2 or later in the person's Chrome; otherwise you get extension_outdated.
Publish a Naver Blog draft as it is — Naver Blog only. Publishes a draft from the blog's draft box exactly as it is in Naver: the extension loads that draft in the editor and presses publish, so edits the person made by hand in Naver are kept. Do not send content. Category, tags and openType are taken from the draft unless you pass them. Returns the same shape as publish (202 with a target that resolves to published, or the bridge object when the extension is offline; wait: true holds until it settles). If that draft was made through uplika (a target whose externalId starts with draft:), the same post record flips to published in…
⟨328 unchanged words⟩ {"key":{"type":"string"},"message":{"description":"follow: message on the mutual request.","type":"string"},"mutual":{"description":"follow: ask for a mutual neighbor relation (Naver). Default false.","type":"boolean"},"tag":{"type":"string"},"type" ⟨8 unchanged words⟩ ","close_conversation","assign","hide_reply","approve_reply","follow"],"type":"string"},"userId": ⟨364 unchanged words⟩
⟨9 unchanged words⟩ from list_accounts.","type":"string"},"askEmail":{"description":"Ask for their email after the tap and store it in contact field `email`. Three tries, then continue without.","type":"boolean"},"buttonTitle":{"description":"Button under theprivateopeningreplyDM, at most 20 characters.","type":" ⟨3 unchanged words⟩ What to send after the tap: text, up to three link buttons, and/or a media id.","properties":{"link":{"description":"Legacy: one url appended to the text. Prefer links.","type":"string"},"links":{"items":{"properties":{"title":{"description":"At most 20 characters.","type":"string"},"url":{"type":"string"}},"required":["title","url"],"type":"object"},"maxItems":3,"type":"array"},"mediaId":{"type":"string"},"mediaKind" ⟨8 unchanged words⟩ "string"}},"type":"object"},"emailMessage":{"type":"string"},"emailRetry":{"type":"string"},"enabled":{"description":"Default false. Prefer leaving ⟨7 unchanged words⟩ person confirms.","type":"boolean"},"followUp":{"description":"Sent followUpAfterMinutes later if none of deliver.links was clicked. Needs openingDm and at least one link.","type":"string"},"followUpAfterMinutes":{"description":"1 to 1380 (23 hours). Default 60.","type":"number"},"keywords":{"description":"Trigger words in the ⟨19 unchanged words⟩ string"},"message":{"description":"The opening DM (privatereply.reply). One message. Required unless openingDm is false.","type":"string"},"name":{"type":"string"},"notFollowingMessage":{"type":"string"},"openingDm":{"description":"Default true. false: deliver goes out as the private reply itself (no button, no window afterwards).","type":"boolean"},"post":{"description":"Our post id, ⟨95 unchanged words⟩ string"}},"required":["accountId","post","message","deliver"],"type":"object"}
⟨145 unchanged words⟩ one they pick. Do not pick for them. Naver Blog goes out through the user's browser extension. If the response carries a bridge object, read bridge.state: offline means that Chrome is closed and the post is queued (up to 7 days); logged_out means Chrome is on but not logged in to Naver. Either way relay bridge.userMessage to the person word for word, do not say it was published, and know that delete_post cancels a queued post and update_post rewrites it before it goes out. If the extension in that Chrome is too old for what you asked (update_post), this returns 422 extension_outdated with the installed and required versions; ask the person to update the extension. Text limits differ per channel: Threads 500 characters ⟨708 unchanged words⟩
⟨933 unchanged words⟩ to 7 days and you should say so.Write theThe bodyasis Markdown:,`##plus`Naver-onlybecomesmarkupaforsectionthingstitle,Markdown`>has`noawordquotationfor (highlighting,`---`styledatables,divider.collages,Plainmaps,paragraphsevents):becomecallbodydescribe_grammartext.withThreeplatformdirectivesnaver_blogplaceforthingstheinsyntaxtheandbody,itseachtraps.aloneReferenceonuploadeditsmedialine:inline with `` an uploaded image, `@video(media:<id>)` an uploaded video, `@card(https://…)` a link card. Put them mid-paragraph and they publish as literal text.`;Idsids you reference this way do not have to ⟨1836 unchanged words⟩
⟨81 unchanged words⟩ ,"field","intent","sentiment","text","clicked"],"type":"string"},"key":{ ⟨212 unchanged words⟩ "type":["string","null"]},"field":{"description":"ask: contact field the answer is stored in (e.g. email).","type":"string"},"flow":{"type":"string"},"instruction" ⟨35 unchanged words⟩ :"object"},"type":"array"},"maxTries":{"description":"ask: default 3. After that the flow continues without a value.","type":"integer"},"maxTurns":{"type":"integer"},"mode" ⟨69 unchanged words⟩ "maxItems":13,"type":"array"},"retry":{"type":"string"},"seconds":{"type":"number"},"then" ⟨18 unchanged words⟩ ,"delay","random","goto","ai","ask"],"type":"string"},"usePersona":{"type":"boolean"},"validate":{"description":"ask: how to check the answer. Wrong answers get `retry`, up to maxTries.","enum":["email","none"],"type":"string"}},"required":["type"],"type ⟨203 unchanged words⟩
⟨114 unchanged words⟩ to connect a channel, then call this again. When a Naver Blog account is selected the response carries bridge with state (online, offline, logged_out), userMessage to relay to the person, and queued; if state is not online, tell the person before publishing.
Look up a channel's body syntax — How to write the body for a channel that has its own markup. Naver Blog has one; every other platform returns not_supported, which is not an error to work around. Call this before writing a Naver Blog body for the first time, or whenever you want something the basics do not cover: highlighting a phrase, a styled table, a collage, an event block, a map with several places. Without a topic you get an overview and the list of topics; with one you get that section in full, including the mistakes that fail silently. The values come from the same grammar the publisher validates against, so what thi…
⟨9 unchanged words⟩ :"string"},"type":"array"},"automation":{"description":"Attach an automation to this post in the same call. template defaults to comment_to_dm; params are that template's params (see list_automation_templates) minus post, which is this post. Created as a draft unless enabled: true. Targets on channels without comments or DMs (YouTube, Telegram, Naver Blog, Bluesky, TikTok) are skipped and named in the response.","properties":{"enabled":{"type":"boolean"},"name":{"type":"string"},"params":{"type":"object"},"template":{"type":"string"}},"type":"object"},"batchId":{"description":"Optional tag (letters, ⟨2764 unchanged words⟩
Approve a held reply (Threads) — Threads only: approve (or ignore) a reply held by reply approval on one of the account's posts. Approving makes it public. Read the queue with pending: true.
Create an automation from a template (draft) — Create an automation from any template in list_automation_templates by passing its params. Creates a draft. For a flow no template covers, build the document yourself and call put_automation on the draft.
Create a comment-to-DM automation (draft) — The most common automation: when someone comments on a post, DM them. **Creates a draft; nothing goes out until enable_automation.** How Meta works: a DM cannot be started by the account. The only automatic door is a private reply to a comment, one per comment, within 7 days of the comment, and once the person answers the 24-hour window opens for the rest. Instagram can check whether the person follows the account (requireFollow); Facebook cannot, so requireFollow is rejected there. Threads has no DMs at all; use create_automation with comment_public_reply. Files go through media_presign firs…
Stop an automation — Stop an automation. Runs already waiting for a button stay waiting but nothing new starts.
Turn an automation live — Turn an automation live. **This is the moment messages start going to real people.** Confirm with the person first. Refuses with reconsent_required if the account was connected before DM permissions, and warns if the account is not subscribed to webhooks.
Read one automation — One automation as a document: triggers, nodes, start. Also returns version, which put_automation needs, and templateParams when the flow still has its template shape.
Read a contact — One contact: name, username, whether they follow the account and whether it follows them (Instagram only, and only for people who have messaged), follower count, tags, opt-out, and when the messaging window closes. Follower lists do not exist on any channel; this is the closest thing.
List an automation's runs — Runs of one automation with their status and a step log. Status names why a run stopped: blocked_window (24-hour window closed), blocked_opt_out, blocked_paused (a person is handling that conversation), blocked_burst, failed_channel, expired, superseded, waiting, done.
List automation templates — Ready-made automation templates with the params each one takes. Read this before create_automation. Every template lists the channels it works on. The most used one is comment_to_dm: a comment on a post gets one private reply with a button, and tapping it delivers a link or file inside the messaging window.
List automations — Automations on the connected accounts: name, channel, whether it is live or a draft, triggers, run count. Use get_automation to read one flow's document.
List inbox conversations — The inbox: DM conversations on Instagram and Facebook and mention threads on Threads. Each item says whether the 24-hour window is open and how long is left. state: open (default) or closed. kind: dm or mention.
List Threads mentions — Threads posts that mention the connected account, delivered by webhook. Same shape as list_conversations with kind mention. Reply with send_dm, which posts publicly.
Replace an automation's flow — Replace an automation's whole document. Pass the version you got from get_automation; a stale version is refused with version_conflict so a concurrent edit is not overwritten. Node types: send (mode window | private_reply | public_reply), condition, action, delay, random, goto, ai. Waiting is a send node with buttons and next: null; the tapped button's next continues. A loop must pass through such a wait. Validation problems come back with paths. Changing a live flow changes what goes out to people.
Read a conversation — One conversation with its recent messages and the contact: name, whether they follow the account (Instagram only), tags, opt-out. AI drafts waiting for approval show as status draft.
Send a message in a conversation — Send a message in a conversation as the account. **Only inside the 24-hour window** after the person's last message; outside it the call is refused with window_closed and nothing can be done until they write again. On a mention thread (Threads) this posts a public reply. Pass draftId to send an AI draft that was waiting for approval. Sending pauses automations on that conversation for 30 minutes.
⟨39 unchanged words⟩ that, the draft is already in Naver. Naver Blog asks which form to publish with, every post: if options.naver_blog.form is missing this returns 400 naver_form_required with forms (id, name, when). The form is the house style to use when the person has no style of their own, so read the conversation first. If they dictated the structure or you laid it out yourself, pass options.naver_blog.layout: "as-is" and the markdown goes out unchanged. Otherwise show them the forms (naver_layout with forms: "all" lays every one out side by side) and call again with the one they pick. Do not pick for them. If nothing failed you get nothing_to_retry. Only applies ⟨58 unchanged words⟩
⟨36 unchanged words⟩ that, the draft is already in Naver. Naver Blog asks which form to publish with, every post: if options.naver_blog.form is missing this returns 400 naver_form_required with forms (id, name, when). The form is the house style to use when the person has no style of their own, so read the conversation first. If they dictated the structure or you laid it out yourself, pass options.naver_blog.layout: "as-is" and the markdown goes out unchanged. Otherwise show them the forms (naver_layout with forms: "all" lays every one out side by side) and call again with the one they pick. Do not pick for them. If the Naver editor shows a "missing image ⟨112 unchanged words⟩
⟨20 unchanged words⟩ do not guess which channel the person meant. Naver Blog caps how many posts one ID publishes; when it does, this returns 429 naver_rate_limited with retryAfterSeconds. Do not call again before that, the draft is already in Naver. Text limits differ per channel: Threads 500 characters ⟨131 unchanged words⟩ Blog has no carousel and places up to5040 images in the post itself, TikTok groups ⟨531 unchanged words⟩
⟨1213 unchanged words⟩ "minimum":10,"type":"number"},"form":{"description":"Form preset for the template layout (default photo-story). GET /v1/naver/forms lists the presets; naver_layout with forms: \"all\" shows them side by side.","enum":["bubble-info","divider-tutorial","photo-review","place-list","news-event","table-explain","mood-review","photo-story"],"type":"string"},"layout":{"description":"template (default) reshapes the body into the blog's house form before it goes out: #/## titles become underlined quote headings, a thin rule sits between sections, and photos you did not place with media: references go one per section (leftovers pair up at the end). Text is never changed. Call naver_layout first to see the result. Use as-is when the person dictated the structure or you placed everything yourself.","enum":["template","as-is"],"type":"string"},"openType":{"description":"Who can see it. ⟨1444 unchanged words⟩
⟨65 unchanged words⟩ post itself.","type":"string"},"secret":{"description":"Naver Blog only: post it as a secret comment that only the blog owner can read. Other channels refuse it.","type":"boolean"},"workspaceId":{"description":"Which workspace this is ⟨41 unchanged words⟩
⟨8 unchanged words⟩ Targets that already went out are left alone. Naver Blog caps how many posts one ID publishes; when it does, this returns 429 naver_rate_limited with retryAfterSeconds. Do not call again before that, the draft is already in Naver. If nothing failed you get nothing_to_retry. Only applies ⟨58 unchanged words⟩
⟨7 unchanged words⟩ it goes out: content, mediaIds, accountIds, Naver Blog caps how many posts one ID publishes; when it does, this returns 429 naver_rate_limited with retryAfterSeconds. Do not call again before that, the draft is already in Naver. If the Naver editor shows a "missing image" error when you open an already-published post for editing, calling update_post on that post rewrites it in place and fixes it; the URL stays the same. options, scheduledAt or draft. Fields you leave out ⟨38 unchanged words⟩ changes. Posts that already went out return post_not_editable:,publishexcept Naver Blog: anewpublishedoneNaverinstead.post is rewritten in place (same URL, same logNo) when you pass content, mediaIds or options — list the media ids again to keep the photos.
Follow a blog — Follow an account on the channel. Naver Blog only today: adds the blog as a neighbor. mutual: true sends a mutual-neighbor request that the other blog has to accept, so the result is pending until they do; without it the blog is added as a plain neighbor right away. Already a neighbor comes back as already. There is no unfollow.
Like a post or a reply — Like a post, or a reply under it when you pass replyTo. Idempotent: if it is already liked the call succeeds with already: true and nothing is toggled. There is no unlike. Naver Blog only today; other channels answer not_supported. On Naver Blog the post can belong to another blog: pass its link and we act as the connected account. Naver ignores liking your own post, and that comes back as naver_not_allowed rather than a fake success.
Preview the Naver Blog layout before publishing — Naver Blog only. Shows how publish will lay the body out before anything goes out. By default (layout: template) publish reshapes the markdown into the blog's house form: #/## titles become underlined quote headings, a thin rule sits between sections, photos you did not place with media: references go one per section and the rest pair up at the end. Text never changes. Call this with the same content and mediaIds you will publish, show the person the result if they care about structure, then publish (it applies the same layout) or pass options.naver_blog.layout: "as-is" to publish exactly wha…
before
—after
{"properties":{"id":{"description":"A publish id (post_…), the post's own id on the channel, or a post link (the URL you would open in a browser to see it). Links work for posts written in the channel's own app too, as long as the account is connected here.","type":"string"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["id"],"type":"object"}before
—after
{"destructiveHint":true,"readOnlyHint":false,"title":"Delete a post"}before
—after
List platform limitsbefore
—after
Every channel and its rules: character limit, whether media is required, image and video limits, and what state the channel is in. Read this instead of guessing a platform's limits. status is one of three: live means anyone can connect; beta means the channel is in platform review and only accounts registered as testers on our app can connect yet, though publishing works normally for those accounts; soon means it is not connectable at all. charCount tells you how that channel counts a character, so you can check the length before calling publish instead of after it fails. options is the JSON Schema of what options.<channel> takes on publish: which fields exist, which are required, and the allowed values. Read it instead of guessing a channel's settings.before
—after
{"properties":{"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"type":"object"}before
—after
{"readOnlyHint":true,"title":"List platform limits"}before
—after
List posts published herebefore
—after
Recent publishes made through us and the per-target status of each, scheduled and draft posts included (their status says so and scheduledAt says when). Newest first, 20 by default. Pass limit for more or fewer, up to 100. hasMore means the list was cut short; pass the returned nextBefore as before to keep going. To answer "what is scheduled this week", pass status scheduled with since and until and sort scheduled. Posts that already existed on the channel are not here. Use list_channel_posts for those.before
—after
{"properties":{"before":{"description":"A publish id from a previous page's nextBefore. Returns the ones older than it.","type":"string"},"limit":{"description":"1-100, defaults to 20","type":"number"},"since":{"description":"YYYY-MM-DD or ISO 8601. Only posts dated at or after this. A scheduled post is dated by its scheduledAt, a sent post by when it was created.","type":"string"},"sort":{"description":"scheduled orders by the post's date ascending (soonest first) and drops paging. Omit for newest first.","enum":["scheduled"],"type":"string"},"status":{"description":"Narrow to one or more statuses, comma separated: scheduled, draft, publishing, published, partial, failed or cancelled. Omit for everything.","type":"string"},"until":{"description":"YYYY-MM-DD or ISO 8601. Only posts dated at or before this (a date means the whole day).","type":"string"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"type":"object"}before
—after
{"readOnlyHint":true,"title":"List posts published here"}before
—after
Publish a postbefore
—after
Post to social channels. Channels open today: threads, instagram, youtube, facebook, bluesky, telegram, naver_blog, tiktok. Get accountIds from select_channels — do not guess which channel the person meant. Text limits differ per channel: Threads 500 characters, Instagram 2200 characters, YouTube 5000 UTF-8 bytes, Facebook 63206 characters, Bluesky 300 graphemes and 3000 UTF-8 bytes, Telegram 4096 characters (1024 with media attached), Naver Blog 30000 characters, TikTok 2200 characters. Over the limit nothing goes out to any channel, so shorten it before calling. Images and video both work on the channels that take them. How several items sit in one post differs per channel: Threads groups up to 20 items in one post, Instagram groups up to 10 items in one post, YouTube has no carousel and places up to 0 images in the post itself, Facebook groups up to 10 items in one post, Bluesky has no carousel and places up to 4 images in the post itself, Telegram groups up to 10 items in one post, Naver Blog has no carousel and places up to 50 images in the post itself, TikTok groups up to 35 items in one post. YouTube is different: it takes exactly one video, no images, and it needs options.youtube.title. Its description is measured in UTF-8 bytes, so Korean and Japanese cost three per character. Instagram cannot publish text alone: every post needs at least one image or video. A single video becomes a reel there. Set options.instagram.contentType to story for a story; stories show no caption. Non-JPEG images are converted for Instagram automatically. Facebook publishes to a Page, never a personal profile. options.facebook.link makes a link post (no media alongside), and a single video becomes a reel (3-90 seconds). Bluesky counts graphemes, not characters, and also caps UTF-8 bytes, so a post of 300 emoji can fail on the byte limit. Set options.bluesky.langs to the language of the text (1-3 BCP-47 codes like ["ko"]): without it the post never appears in language-scoped feeds, and Bluesky has no post editing to fix it later. Links, @mentions and #hashtags in the text are made clickable for you, and a link gets a preview card, so write the URL plainly. There are three ways to get a media id, pick by where the file is: media_from_url when it already has a public https address, media_upload_link when it is on the person's own device, media_presign plus media_complete when you can PUT the bytes yourself. Then pass the media ids here. On Naver Blog the body can also place media itself with  and @video(media:<id>), each on a line of its own. Ids you reference that way are picked up even if you leave them out of mediaIds, and media you pass but never reference goes at the end of the post. Those references only work when every target is Naver Blog: other channels would publish the markup as literal text, so we refuse instead. This publishes immediately unless you pass scheduledAt (we hold the post and send it at that time, on every channel) or draft: true (nothing goes out; the person or update_post finishes it later). scheduledAt needs a timezone offset, 10 minutes to a year out; ask the person which timezone they mean instead of guessing. A scheduled or draft post comes back with status scheduled or draft, can be changed with update_post, sent early with publish_now, and dropped with delete_post. Every channel also takes options.<channel>.content to send that channel a different text than the shared content, which is how Naver Blog Markdown and a 500-character Threads post fit in one call. On YouTube we pass privacyStatus through as you set it and report back what YouTube actually applied, so read the warning on the result instead of promising the person a visibility we did not confirm. Returns while the post is still publishing. The permalink is null at that moment. Call get_post with the returned id to see the final status and link. Pass wait: true to hold the response until it is really out — then you can tell the person it is posted instead of guessing. For a long post use threadItems instead of publish-then-reply: we keep the order and wait for each piece to land before sending the next one.before
—after
{"properties":{"accountIds":{"description":"Account ids from select_channels","items":{"type":"string"},"type":"array"},"batchId":{"description":"Optional tag (letters, digits, - or _) to group posts made together, for example the same text sent to two workspaces. Pass the same value on each call.","type":"string"},"content":{"description":"Post text","type":"string"},"draft":{"description":"true saves the post as a draft on uplika without sending anything. Use it when the person wants to review before it goes out. They finish it in the dashboard, or you call update_post and publish_now.","type":"boolean"},"mediaIds":{"description":"Media ids from media_presign (confirmed with media_complete), media_from_url or media_upload_link. On Naver Blog you can leave out ids the body already points at with media:<id>; we pick those up from the text.","items":{"type":"string"},"type":"array"},"options":{"description":"Per-channel settings, keyed by channel id. Only the channels you are posting to need an entry. Every channel takes content to override the shared text for that channel alone.","properties":{"bluesky":{"description":"Bluesky settings. Optional, but langs is worth setting on every post.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"langs":{"description":"The language of the text, 1-3 BCP-47 codes like [\"ko\"] or [\"en\", \"ko\"]. Feed generators filter on this, so without it the post never appears in language-scoped feeds. Bluesky has no post editing, so this cannot be fixed after publishing. Set it to the language the text is actually written in.","items":{"description":"One BCP-47 code.","pattern":"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$","type":"string"},"maxItems":3,"type":"array"}},"type":"object"},"facebook":{"description":"Facebook Page settings. Optional: plain text publishes without it.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. A Facebook story is rejected if you send any text: publish the story with no content, and put the words in a feed post instead. A story also takes exactly one image or video and no first comment.","enum":["feed","story","reel"],"type":"string"},"firstComment":{"description":"A comment we post right after publishing. Not on stories, and not on scheduled posts. If it fails the post still goes up, with a warning.","type":"string"},"link":{"description":"Makes a link post with a preview card. Cannot be combined with media.","type":"string"},"scheduledPublishTime":{"description":"ISO 8601 time to publish a feed post later, 10 minutes to 28 days from now. Feed posts only, not reels or stories. Cannot be combined with firstComment (a scheduled post is not live yet, so nothing to comment on). This is Facebook's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"title":{"description":"Reel title, separate from the caption.","type":"string"}},"type":"object"},"instagram":{"description":"Instagram settings. Optional: a plain post with media works without it.","properties":{"collaborators":{"description":"Up to 3 public professional accounts to invite as collaborators. They appear only after accepting. Not on stories.","items":{"description":"A public professional account's username, without @.","type":"string"},"maxItems":3,"type":"array"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"contentType":{"description":"What kind of post. Defaults to reel for a single video and feed otherwise. Stories show no caption and take one image or video.","enum":["feed","story","reel"],"type":"string"},"coverMediaId":{"description":"Media id of a JPEG up to 8MB to use as the reel cover. Reels only.","type":"string"},"firstComment":{"description":"A comment we post right after publishing, often used for hashtags. Not on stories. If it fails the post still goes up, with a warning.","type":"string"},"isAiGenerated":{"description":"Set true to label the media as AI-generated on Instagram.","type":"boolean"},"shareToFeed":{"description":"Reels only. false keeps the reel out of the main feed.","type":"boolean"},"thumbOffsetMs":{"description":"Reel cover frame in milliseconds. Ignored when coverMediaId is set.","minimum":0,"type":"number"},"userTags":{"description":"Tag accounts on the media. Photos need x and y between 0 and 1; videos take the username alone. mediaIndex picks the carousel slide.","items":{"description":"One tagged account.","properties":{"mediaIndex":{"description":"Which carousel slide, from 0.","type":"number"},"username":{"description":"Username without @.","type":"string"},"x":{"description":"0 to 1 from the left. Photos only.","type":"number"},"y":{"description":"0 to 1 from the top. Photos only.","type":"number"}},"required":["username"],"type":"object"},"type":"array"}},"type":"object"},"naver_blog":{"description":"Naver Blog settings. Required when a target is a Naver Blog account. Naver Blog has no official API: the post is written by the user's browser extension, so it goes out only while that browser is open. list_accounts tells you whether the extension is online; if it is offline the post queues for up to 7 days and you should say so. Write the body as Markdown: `## ` becomes a section title, `> ` a quotation, `---` a divider. Plain paragraphs become body text. Three directives place things in the body, each alone on its line: `` an uploaded image, `@video(media:<id>)` an uploaded video, `@card(https://…)` a link card. Put them mid-paragraph and they publish as literal text. Ids you reference this way do not have to repeat in mediaIds, and media you pass but never reference goes at the end. When other channels are in the same post, put the Markdown in options.naver_blog.content so the shared content stays plain text.","properties":{"alignCenter":{"description":"Center every paragraph and image.","type":"boolean"},"category":{"description":"The category name instead of its id, exactly as it appears in naverBlog.categories from list_accounts. If the blog does not have it you get naver_category_unknown with the list to pick from; if the person just created it, call refresh_account first.","type":"string"},"categoryId":{"description":"The id of a category on the connected blog. list_accounts returns them under naverBlog.categories. Give this or category (the name); there is no default, and without one Naver files the post under the wrong board.","type":"string"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content. On Naver Blog this is where the Markdown body goes when the other channels get plain text.","type":"string"},"draftOnly":{"description":"Save as a draft in Naver instead of publishing. The user finishes it by hand. Different from draft on the post, which keeps the whole post on uplika.","type":"boolean"},"fontFamily":{"description":"Body font, one of nanumgothic, nanummyeongjo, nanumbarungothic, nanumsquare, maruburi. Omit for the editor default.","enum":["nanumgothic","nanummyeongjo","nanumbarungothic","nanumsquare","maruburi"],"type":"string"},"fontSize":{"description":"Body font size in px, one of the editor's sizes (11-38). Omit for the editor default.","enum":["11","13","15","16","19","24","28","30","34","38"],"maximum":34,"minimum":10,"type":"number"},"openType":{"description":"Who can see it. public (default), neighbor, mutual (mutual neighbors only), or private.","enum":["public","neighbor","mutual","private"],"type":"string"},"scheduledAt":{"description":"Schedule the post inside Naver (ISO 8601, at least 10 minutes from now). The extension sets it in Naver's publish dialog. For scheduling across channels use scheduledAt on the post instead.","type":"string"},"tags":{"description":"Up to 30 tags without the # sign and without spaces.","items":{"description":"One tag without # and without spaces.","type":"string"},"maxItems":30,"type":"array"},"title":{"description":"Required. The post title, up to 100 characters.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"},"telegram":{"description":"Telegram has no channel-specific settings. Note the caption limit: with media attached the text is capped at 1,024 characters instead of 4,096.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"}},"type":"object"},"threads":{"description":"Threads settings. Optional.","properties":{"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"topicTag":{"description":"One topic to tag the post with, like a category. Threads takes exactly one and rejects periods and ampersands in it. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","maxLength":64,"type":"string"}},"type":"object"},"tiktok":{"description":"TikTok settings. privacyLevel is REQUIRED for a normal post and has no default on purpose: TikTok's rules say the person must choose visibility deliberately, so we ask for it instead of guessing. Call get_publish_options first to see which values this account may use right now, whether it can post at all, and its video length limit. TikTok has no text-only posts, cannot mix a video and photos, and has no API for deleting a post or reading comments. While this app is awaiting TikTok's Content Posting audit a direct post can only be SELF_ONLY and comes back with no link and no metrics; if the person wants it public in the meantime, use postMode draft instead.","properties":{"brandContentToggle":{"description":"Declare paid partnership content (\"Branded Content\"). TikTok does not allow branded content to be private, so this cannot be combined with privacyLevel SELF_ONLY.","type":"boolean"},"brandOrganicToggle":{"description":"Declare that the post promotes the creator's own brand (\"Your Brand\"). Leave it out rather than sending false: not declaring and declaring 'no' are different statements.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"disableComment":{"description":"Turn comments off for this post. Cannot be set to false if the creator disabled comments account-wide.","type":"boolean"},"disableDuet":{"description":"Turn duets off for this post. Video posts only.","type":"boolean"},"disableStitch":{"description":"Turn stitches off for this post. Video posts only.","type":"boolean"},"isAigc":{"description":"Declare that the content was generated by AI. Worth setting when you made the video or images.","type":"boolean"},"postMode":{"description":"direct (default) posts to the profile. draft sends it to the creator's TikTok inbox; they open the notification and finish it in the app, choosing the visibility themselves. While this app awaits the Content Posting audit this is the only way to get something out publicly: a direct post can only be SELF_ONLY, but on this path we send no visibility at all. The trade is that it is not on the profile until the person finishes it, so there is no link and no metrics in the meantime.","enum":["direct","draft"],"type":"string"},"privacyLevel":{"description":"Required unless postMode is draft. One of the values that get_publish_options returns for this account, e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. The list differs per account, so do not hard-code it.","type":"string"},"title":{"description":"Required for a photo post, up to 90 characters. A video post has no title field at all and passing one is rejected; a video's text comes from content.","maxLength":90,"type":"string"},"videoCoverTimestampMs":{"description":"Which frame to use as the cover, in milliseconds into the video. TikTok does not accept a cover image file, only a timestamp.","minimum":0,"type":"number"}},"type":"object"},"youtube":{"description":"Required when a YouTube account is in accountIds. The post content becomes the video description.","properties":{"categoryId":{"description":"YouTube category id. Defaults to 22 (People & Blogs). Leave it out unless the person named a category.","type":"string"},"containsSyntheticMedia":{"description":"Set true when the video contains realistic altered or synthetic content, including AI generated footage of real-looking people, places or events.","type":"boolean"},"content":{"description":"Text for this channel only. When set it replaces the shared content for this channel, and the length and media checks use it. Leave it out to use the shared content.","type":"string"},"madeForKids":{"description":"Whether this video is directed at children. This is a legal declaration about someone else's channel. Leave it out unless the person tells you, and the channel's own default applies.","type":"boolean"},"privacyStatus":{"description":"Defaults to private. We pass this through as you set it. Google can still restrict uploads from an API project that is under review, so if the video ends up somewhere else you get a warning on the result saying what YouTube actually applied.","enum":["public","unlisted","private"],"type":"string"},"publishAt":{"description":"ISO 8601 time to make the video public. Only works with privacyStatus private, and only on a video that was never public. This is YouTube's own scheduling; for scheduling across channels use scheduledAt on the post.","type":"string"},"tags":{"description":"Search tags. 500 characters across all of them; a tag with a space costs two extra for the quotes YouTube adds.","items":{"description":"One search tag.","type":"string"},"type":"array"},"thumbnailMediaId":{"description":"Media id of a JPEG or PNG up to 2MB to use as the thumbnail. Long-form only: YouTube does not take custom thumbnails on Shorts, and a vertical video of three minutes or less becomes a Short. The channel also has to be verified before YouTube accepts one.","type":"string"},"title":{"description":"Video title, up to 100 characters. Required. Cannot contain < or >.","maxLength":100,"type":"string"}},"required":["title"],"type":"object"}},"type":"object"},"scheduledAt":{"description":"ISO 8601 time with a timezone offset (2026-09-20T09:00:00+09:00) to send the post at, on every channel. 10 minutes to 365 days from now. We keep the post as scheduled until then; the person can still change or cancel it.","type":"string"},"threadItems":{"description":"Split a long post into a chain instead of calling publish and then reply. The first item is the root and the rest become replies under it, in order. We handle the ordering and the waiting. Each item obeys the character limit on its own. Pass either content or threadItems, not both. If a later item fails, the ones already up stay up and the response tells you where to resume.","items":{"properties":{"content":{"type":"string"},"mediaIds":{"items":{"type":"string"},"type":"array"}},"required":["content"],"type":"object"},"type":"array"},"topicTag":{"description":"One topic to tag the post with, like a category. Only some channels take one, and those reject periods and ampersands in it. If any channel in accountIds does not take topics the whole call is refused, so publish to it separately. On a thread it goes on the first piece only. Leave it out unless the person asked for a topic.","type":"string"},"wait":{"description":"Hold the response until the post is really out. Text waits up to 10 seconds, posts with media up to 90 seconds. If it is still going after that you get the usual publishing response and should poll get_post. Defaults to false. Ignored with scheduledAt or draft.","type":"boolean"},"workspaceId":{"description":"Which workspace this is for. Only needed when the account has more than one — the error tells you the ids when it matters. Leave it out if it is already decided; do not ask the person again.","type":"string"}},"required":["accountIds"],"type":"object"}before
—after
{"destructiveHint":true,"readOnlyHint":false,"title":"Publish a post"}Send a scheduled or draft post now — Send a scheduled or draft post right now instead of waiting. Returns while it is still publishing, like publish; pass wait: true to hold for the result. A draft needs at least one target account first. Posts that already went out return post_not_editable.
Edit a scheduled or draft post — Change a scheduled or draft post before it goes out: content, mediaIds, accountIds, options, scheduledAt or draft. Fields you leave out keep their current value; options you pass replace the whole options object. scheduledAt moves the send time (same rules as publish), null turns it into a draft, and draft: true does the same. A thread only takes schedule changes. Posts that already went out return post_not_editable: publish a new one instead.