Publish a post to X (Twitter)
post_to_xPublish a single post, thread, poll, or media to a connected X (Twitter) account, publicly and immediately. Requires X connected and explicit user approval before sending.
Instructions
Publish to the user’s connected X (Twitter) account — a single post, a post with an image or video render attached, a reply to an existing post, or a whole THREAD (pass thread as an array and each part is posted as a reply to the one before). This PUBLISHES immediately and PUBLICLY — ALWAYS show the user the exact text and get an explicit yes BEFORE calling. Can also run a POLL (2-4 options) instead of media, and restrict who may reply. LENGTH IS THE POSTING ACCOUNT’S, NOT A FLAT 280: 280 characters on an ordinary account, but UP TO 25,000 if that account has X Premium. Hermoso reads the account’s own subscription from X and sends the post rather than refusing something it may be entitled to; if X declines it on length, the reply says so and names the subscription X reported. Over 25,000 is refused for free — that is X’s own ceiling on every account. Text is NEVER truncated. AND A LONG POST IS CHEAPER THAN A THREAD: it is ONE billed X call where the same words split across five posts are five, so prefer one long post over threading when the account has Premium. Only the first ~280 characters show in the timeline; the rest sits behind “Show more”. Write altText whenever you attach a render. COSTS CREDITS: X charges per API request, so every post in a thread is billed, and a post containing a LINK costs roughly 13× one without — mention the cost before publishing a long thread. Each profile also has a rolling 24-hour ceiling on what it can spend at X, and a request that would cross it is refused WHOLE before anything publishes, so a batch of posts is bounded rather than open-ended. THIS TOOL POSTS ORGANICALLY — it does not create an ad campaign. X ads are built with create_x_ads_campaign -> create_x_ads_line_item -> create_x_ads_promoted_tweet, and a promoted post needs a post id, so publish here first and promote that post. Needs X connected (Settings > Connectors > X).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works. | |
| poll | No | run a poll on the post. X does not allow a poll and media on the same post, and a poll cannot ride on a thread. | |
| text | No | the post text. 280 characters without X Premium, up to 25,000 with it — write the full thing, it is never truncated. Use this OR thread, not both. | |
| brand | No | WHICH PROFILE this post belongs to — id or exact name from list_brands (a shared workspace: its profile id). Beats the connection's pin for THIS CALL ONLY; a name that matches no profile, or two, is REFUSED and nothing is posted. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| thread | No | a thread: each string is one post, published in order, each replying to the previous. Max 25. Each part follows the same length rule as `text`, and on an X Premium account ONE long post is usually both better reading and cheaper than a thread. | |
| account | No | WHICH connected account of this channel to post as — its @handle or id from list_connector_accounts. Needed only when the profile has more than one x account connected (several and none named is refused by name, never guessed); omit when there is one. | |
| altText | No | accessibility description of the attached media, max 1000 characters — write one whenever you attach a render. Costs a small extra amount: X bills one metadata write PER media. With SEVERAL media, pass an ARRAY aligned to their order — X attaches alt text per media id, and a single string describes only the FIRST one (X renders up to four media as a GRID, not a carousel, so one sentence would be wrong for the other three). | |
| subject | No | WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook. | |
| imageUrl | No | alias of mediaUrl for an IMAGE — same as passing it as mediaUrl | |
| mediaUrl | No | a Hermoso render (image or video) to attach to the first post — pass its served URL, or an upload_file url for external media | |
| videoUrl | No | alias of mediaUrl for a VIDEO — same as passing it as mediaUrl | |
| mediaUrls | No | UP TO FOUR Hermoso-hosted media attached to ONE post — X’s own schema caps media_ids at 4. X renders them as a GRID: every image visible at once, nothing to swipe to. That is NOT a carousel, and a numbered "1/6 · SWIPE" slide deck must still not be sent here — it would publish as a grid and the "swipe" instruction would make no sense. Order decides the layout. On a thread the media rides the FIRST post; give each later part its own post to attach more. Mutually exclusive with mediaUrl, and more than 4 is refused before anything is uploaded. | |
| replyToId | No | numeric id of an existing X post to reply to. X ONLY ACCEPTS AN API REPLY TO A POST THAT MENTIONS THIS ACCOUNT OR THAT THIS ACCOUNT WROTE (X policy since Feb 2026, every tier below Enterprise): a cold reply under a stranger's post is refused by X with "You can only reply to or quote posts where you are mentioned or are the author" and nothing is posted. Reply to those from x.com itself; use this for replies in your own threads and to people who tagged you. | |
| communityId | No | publish into an X COMMUNITY instead of the main timeline — the number in the community’s own URL (x.com/i/communities/<id>). The connected account must be a MEMBER of it; X answers a non-member and a non-existent id with the same refusal and does not separate them. | |
| quotePostId | No | numeric id of a post to QUOTE — X renders the quoted post inside yours. This is NOT replyToId: a reply sits under the original in its thread, a quote stands alone on your own timeline with the original embedded, which is the one you want for commentary. X makes a quote mutually exclusive with media and with a poll (their own schema), and a quote is billed at the higher LINK rate because X appends the quoted post’s t.co URL whatever your text says. Same X rule as replyToId: a quote of a post that does not mention this account is refused by X. | |
| platformCover | No | VIDEO COVER. X has no cover setting and shows the video’s FIRST frame, so by default Hermoso checks it and, only when that frame is blank (a template ad’s empty opening card), sends X a copy with the first frame replaced by the video’s best frame. Your Library file is never changed. true = send the file exactly as it is. | |
| replySettings | No | restrict who can reply — omit for everyone, which is the right default for a brand post | |
| paidPartnership | No | label the post a PAID PARTNERSHIP on X — the same disclosure Hermoso already ships for TikTok. OPT-IN ONLY: set it when the post is sponsored, gifted or otherwise paid for, and never assume it on the user’s behalf. |