One URL. Every agent you already talk to.
vibedraft runs as an MCP connector. Your own agent does the drafting, on your own subscription. vibedraft supplies the voice profile, the live signals, the queue, the scheduling and the metrics. It holds every post until you say yes.
https://vibedraft.app/api/mcpThe same URL in all four clients. Each one opens a vibedraft consent screen the first time; approving it issues a token scoped to your account, and you can revoke it from Settings at any time.
Claude Code
The terminal. One command, then one browser tab.
claude mcp add --transport http vibedraft https://vibedraft.app/api/mcpclaude/mcp/mcp finishes the OAuth handshake in your browser and shows vibedraft as connected. Add --scope user to the first command to make the connector available in every project instead of the current one.
Claude
claude.ai and the Claude desktop app. Custom connectors need a Pro or Max plan.
https://vibedraft.app/api/mcpThe desktop app and claude.ai read the same connector list, so adding it in one adds it in both. Revoke it from Settings at any time.
Codex
The Codex CLI and the Codex IDE extension.
[mcp_servers.vibedraft]
url = "https://vibedraft.app/api/mcp"
auth = "oauth"codex mcp login vibedraftA remote server goes in the config file rather than through codex mcp add, which takes a launch command for a local process. auth = "oauth" is what makes Codex open the sign-in instead of reporting a failed connection. In the Codex IDE extension, the same server is added from the gear menu under MCP servers → Add server.
ChatGPT
The ChatGPT desktop app, on a plan that allows custom MCP servers.
https://vibedraft.app/api/mcpvibedraft.~20 secChatGPT on the web reaches custom MCP servers only through developer mode, and what a server is allowed to expose there is narrower than in the desktop app. The desktop app is the path to use today.
Cron jobs skip OAuth.
For anything that runs without a browser, take a personal access token from Settings and call the REST API directly.
curl https://vibedraft.app/api/v1/scheduled-posts \
-H "Authorization: Bearer vd_pat_..."Tell it what happened.
Marketing events are things that happened to your product: a milestone crossed, a feature shipped, a customer won or churned, a theme in support. Record them as they happen and vibedraft has something real to write from later. Nothing posts, nothing is scheduled, and no credits are spent.
mark that we crossed 100 paying userscurl -X POST https://vibedraft.app/api/v1/events \
-H "Authorization: Bearer vd_pat_..." \
-H "Content-Type: application/json" \
-d '{"type":"feature.shipped","ref_id":"dark-mode","title":"Dark mode is live"}'title is required on these event types and body is optional. Write the title in plain words a stranger could read, “We crossed 100 paying users” rather than “milestone-100”. It is the only text later stages have to work with, so an event without it cannot be judged worth posting, cannot be checked against the topics you never want posted about, and cannot be read by you when vibedraft asks.
Types: milestone.crossed, feature.shipped, customer.won, customer.churned, support.theme.
ref_id is a stable id for the thing itself, so the same event recorded twice is recognisable.
What your agent can do.
A plain post costs 1 credit; a post containing a URL costs 13. Reading metrics is free.
get_onboarding_stateWhether your login can use VibeDraft yet and the one next thing to do about it — a tool to call, or a link to open when the step needs a browser.
start_intakeBegin or resume the onboarding interview. Returns the next thing to find out and how to ask it.
answer_intakeRecord what you said, in your own words, and get the next prompt, or that the interview is complete.
restart_intakeRun the interview again when something about you has changed. Your old answers stay as history; the questions get asked fresh.
get_intake_statusWhere the interview stands, and your 7-day activation plan once it's complete.
get_account_contextEverything VibeDraft knows about you and your business, as one block, plus the few things most worth learning next and why each matters.
capture_contextRecord things that came up in conversation: a fact, a number with its date, a preference, a correction, something a customer said. Vague values are sent back with what would make them specific.
import_contextPaste the memory another assistant kept about you (Claude, ChatGPT) and VibeDraft files what is durable under the right slots, marked imported, and proposes any goals it states.
note_turnHand VibeDraft what you just said, verbatim. It keeps the facts, numbers, preferences and corrections worth keeping, proposes any goal you stated, and tells the agent what it kept so you can undo it.
search_journalSearch everything you have said to any connected assistant or in Compose, kept whole with its date. The record under the memory summary: what you said about pricing, when you started something, what you decided.
confirm_contextMark lines the agent believed about you as confirmed, once you have agreed.
forget_contextRetire a line about you. Kept as history, no longer read.
list_accountsEvery account this login drives — X handles, Instagram, TikTok and Threads accounts — with id, channel, status and which one this connector is acting as.
switch_accountPoint this connector at a different account on either channel. Saved per connector, so your other clients are unaffected.
get_voice_contextYour voice, resolved for the active account's channel and format: profile, brand layer, channel conventions and limits, hard rules, drafting rules, and what is still missing. Can also carry what you said this chat.
update_voice_profileRewrite your voice profile from chat. Full replace, versioned, and free — the same document /voice edits. A document that omits sections you already have is rejected rather than applied.
get_signalsActive signal briefs: what's moving right now in your niches.
list_bookmark_foldersYour X bookmark folders, with the id analyze_bookmark_folder takes.
list_bookmarksEverything you have bookmarked on X, newest first, with each post's author, metrics, folders and link. Free in credits; a tenth of a cent per bookmark on X, once a day.
analyze_bookmark_folderRead a bookmark folder of posts you admire and get the evidence for why each did well and what they share: metrics, each post's multiple over its author's normal, what it quoted or answered, the replies under it on request, its trajectory over time, and the folder's structural features against a control group of tracked creators. Free in credits; posts are read from X once (about half a cent each), stored, and re-read daily while young.
get_goalThe handle's goal archetype, and whether a human has confirmed it. The archetype selects the ranker's weight vector.
set_goalSet the goal by picking an archetype or by classifying the user's own words.
confirm_goalRecord that the user agreed with a classified archetype.
add_objectiveRecord an account-wide goal in the user's words (e.g. more people trying vibedraft). Every agent reads these as context; the reply judge weighs posts against them.
create_short_linkMake a tracked short link (vibedraft.app/l/<slug>) for one place on X: clicks, the signups they become and the paid checkouts after are attributed to it. Given a lead magnet and a channel, it links to that magnet and is not counted toward the 50-link cap.
get_link_reportEvery tracked link with clicks, visitors, signups and paid revenue, so the next link goes where the last one worked.
list_lead_magnetsThe free resources you offer in posts (comment a keyword, get the link): title, page, keywords and channels.
create_lead_magnetRecord a free resource you offer in posts: its title, its page on vibedraft.app, the keywords people comment, the channels and the sign-up ask at the end of its page. Nothing is posted.
update_lead_magnetChange a lead magnet's title, page, keywords, channels or end-of-page sign-up ask, or archive it. Its links keep redirecting.
get_magnet_reportPer lead magnet and per channel: links, clicks, visitors, app sign-ups and waitlist sign-ups it brought, with waitlist sign-ups split by form, plus the posts that offer it and the replies that asked for it (by channel, keyword or judge).
set_magnet_judgeTurn the lead magnet request judge on or off for an Instagram or Threads account and set its threshold. Keyword replies always count; the judge reads the rest and stays off until its eval clears.
analyze_productStart a read of a product's site, or return a brief from the last 24 hours: the product card, the memory lines it grounds (each with a verbatim quote), and the questions it leaves open. refresh: true reads again inside the day, for the product's owner, at most every 30 minutes. A start is rate limited and costs model calls; it writes nothing on the account.
get_product_briefA stored discovery brief for a site, a run id, or this login's newest: card, grounded lines, open questions, brand profile, TrustMRR match, chat answers.
list_product_briefsThis login's discovery briefs, including ones run before signup on the same visitor cookie. Newest first, one per site.
save_productFile a product and the corrected lines about it, through the same memory judge as every capture; returns the gaps still to ask.
set_lead_rulesWho is not a customer of the active account's product, and its direct competitors: what the lead judges read beside 'Who it is for'. Confirm the lists with the user first.
archive_objectiveRetire one account goal by id. Kept, not deleted.
list_tracked_handlesThe accounts Radar listens to for the active handle, whether each follows the handle back, and what tracking does and costs.
track_handleAdd an X account to the list Radar reads new posts from (about half a cent a post, a few cents a month). Nothing changes on X.
untrack_handleStop reading an account's posts. Posts already read stay.
get_engage_queueWho to reply to right now, ranked, with the reason each card ranked. Writes: every card returned is logged with the probability it was shown at.
list_leadsThe account's leads by tier (hot, warm, watch; not_a_lead and noise only when asked): identities, product, fit score, status, and whether they published an email (has_email). Filter by tier, status or score.
next_actionsWhat to do right now across leads, replies to your X posts and new prospects: people waiting on you first, then the rest by printed points, each with a reason, a channel and a deadline. Nothing is sent.
unpause_placeResume a place a removed reply paused (r/<subreddit>, x or instagram): removals there before now stop holding moves back.
snooze_actionHide one next action until a time, or for good. Refuses a key next_actions did not return.
set_reminderRemind the founder about a lead at a time, with a note. From a day before it is due it ranks in next_actions above every other scored move, never above people waiting on him; it clears when he next replies or DMs the lead. Nothing is sent.
clear_reminderDismiss a lead's live reminder.
get_leadOne lead's full record: identities, links and where each was found, the contacts they published themselves (email or Discord, with the page each was read from), what they wrote, their pain in their words, their product's brief as the lead page shows it (one-liner, who it is for, buyer quotes, where buyers talk, the first move), the timeline.
update_leadChange a lead's status or notes, mark a channel they cannot be reached on, mark a recorded reply that answered someone else, or record a fit verdict with a reason; written to its timeline.
confirm_lead_icpRecord who a lead said their product is for, with their exact words (checked against what they wrote): stored on the lead only (never on a brief). Sends nothing.
write_lead_value_frameWrite the one opening line above a lead's value pack (get_lead value_pack: their buyers' own words, threads from the last 30 days, one checkable tip, each linked). A model writes only the line; code checks it. Stored on the lead. Sends nothing.
record_lead_activityRecord outreach drafted or sent, a reply received, a note, or a touch (a follow, like, reply or DM on Reddit, X or Instagram) on a lead's timeline, one event per message; moves the lead's status forward. Sends nothing.
update_lead_factsWrite what the founder knows about a lead (founder, title, company, stage, revenue note, custom details, notes), through the same records as the lead page, so his value wins. No files.
get_research_budgetThe account's daily budget for paid lead research and what the last 24 hours spent, by new leads, backlog and Research now (Settings, Product).
set_research_budgetSets the account's daily budget for paid lead research, in dollars (Settings, Product); the free reads are never limited.
research_lead_nowRuns the paid research for one lead at once on the stronger model (the lead page's Research now), counted against the account's daily research budget; refused once the day's budget is used.
set_lead_tierThe founder corrects a lead's tier (hot, warm, watch, not_a_lead) with his words verbatim; his tier outranks the lead judge until he changes it.
get_email_activityWhat happened to the emails sent to one email id, lead, address or group email (send_id, with each recipient's status): status, delivery (delivered, bounced, marked as spam) with its history, Resend's own status when no delivery event arrived, and the latest email health check.
get_email_settingsEach of your waitlists with the postal address its emails actually carry and where it comes from (the list's setting or the server default), who it sends as, and a warning when the address is missing or incomplete.
list_waitlist_tagsThe tags you put on your waitlist sign-ups: one person's, by email, or every tag in use with how many people carry it.
add_waitlist_tagTag one of your waitlist sign-ups, found by email, the same as adding a tag on the Waitlist page.
remove_waitlist_tagTake a tag off one of your waitlist sign-ups, found by email.
remove_waitlist_signupRemove someone from your waitlist, found by email. Cannot be undone; emails already sent stay in your email log.
import_list_entriesImport your own addresses into one of your lists, as CSV text or rows (at most 500), with a label for where they came from. Already on the list or unsubscribed: skipped and counted. Nothing is sent; preview counts first.
set_email_settingsChange the postal address a waitlist's emails carry. Empty clears the list's setting; with no server default, one-to-one, onboarding and that list's emails stop sending.
create_newsletterCreate a newsletter list: name, slug, sender on a domain verified in Resend, reply-to, postal address, form origins, and the account its subscribers become leads on. Every sign-up confirms by email first.
update_newsletterChange a newsletter's name, sender, reply-to, postal address, form origins, confirm setting, daily confirm cap, lead account or status. A new sender is checked against Resend first; the slug never changes.
draft_emailDraft one first email, in your voice, to the contact email a lead published, about the problem they posted. Help first, never a pitch. Saved as a draft; nothing is sent.
edit_emailSave your own edit to a drafted email's subject and message. Saving un-approves it, so it is previewed again before any send. Nothing is sent.
preview_emailExactly what a drafted email would send, footer included, and a one-time preview_id valid 10 minutes. Nothing is sent.
send_emailSend one one-to-one email, only from a preview_email you approved in that chat. Refuses an unsubscribed address, a lead already emailed and a spent private-message limit.
mark_email_not_sentAfter you checked Resend and found no such email: mark a send that never confirmed, and is too late to retry, as not sent, so a new email can be drafted. Nothing is sent.
draft_group_emailDraft one email to your own people: everyone opted in, a People filter, a saved segment, or chosen waitlisters and app users. Never a found-contact lead. Nothing is sent.
edit_sendChange a group email's subject, message or audience before it goes. Saving un-approves it, so it is previewed again.
preview_sendWho a group email reaches, who is left out and why, the exact email one person gets, how long it takes on the daily room, and a one-time preview_id valid 10 minutes. Nothing is sent.
approve_sendSend a group email, only from a preview_send you approved in that chat. Each person once, inside the shared daily room; the rest wait for the next room.
send_testSend yourself one test copy of a group email, marked [Test], never counted in the send.
list_email_sendsYour group emails, newest first, each with its recipients by status.
draft_lead_replyDraft a first reply to the post that made someone a lead, checked against the subreddit rules vibedraft recorded on a dated snapshot; recorded, never posted.
list_my_handlesThe user's own usernames per channel, used to spot their replies to leads and skip their own posts.
update_my_handlesAdd or remove the user's own usernames on a channel (several allowed).
add_lead_linkAttach their product, site or a social profile to a lead; a product added here outranks the agent's guess.
remove_lead_linkHide a link from a lead on the founder's word (a product that is not theirs, a site or an account), in every spelling; nothing is deleted.
list_feed_cardsCards in the Feeds review queue, with the arm that surfaced each and its link.
review_feed_cardApprove or skip a Feeds card; approving makes the author a lead. Posts nothing.
record_engage_decisionRecord that a queue card was replied to or skipped. A skip is a training signal, not a discard.
take_planClaim a queue card's plan before drafting, so two agents never answer the same post.
get_daily_packVoice + signals + upcoming schedule as one bundle, so a session can start with a single call. Can also carry what you said this chat, so it is remembered.
get_pricingCredit costs, available plans, and your current balance.
list_inboundComments on your own Instagram posts, your Instagram DMs, and replies to your own Threads posts, read every 5 minutes; by default only people who are not a lead yet. Read-only.
check_inbound_nowRead one Instagram or Threads account's comments and replies right now, then check magnet posts for people asking; per post it shows Meta's count, what Meta returned and what was stored. Once a minute per account; read-only toward Meta.
get_post_metricsImpressions, likes, replies and bookmarks for posts you've published.
search_my_postsSearch your own published posts by keyword, so a draft does not repeat something you already said.
search_trending_postsWhat creators you track are posting on a topic, ranked by engagement — evidence of what lands, not phrasing to copy.
growth_postsThe posts that drove people to your profile, by rate rather than reach — the closest thing to a growth ranking.
what_makes_a_top_postWhat separates your top posts from your bottom ones on each channel, with the numbers and an honest 'inconclusive' where the data cannot tell.
post_shapesWhich shapes of post (asks, takes, teaching, pitches, proof, events) have earned more or less depth than your usual on each channel, every claim with its number of posts.
set_threads_leadsTurn Threads lead finding on or off for an account, the same switch as in Settings.
update_productEdit a product's name, one-liner, URLs, handle, brand and content targets; only what you send changes.
archive_productArchive a product so it stops appearing in briefs, drafting and lead finding; its history is kept.
update_objectiveEdit one of your account goals, including its measure and target, in the units you think in.
edit_contextCorrect a line of what VibeDraft remembers about you, keeping its history.
remove_blog_targetRemove one of your own blog destinations; published posts stay where they are.
disconnect_metric_sourceDisconnect a PostHog or Vercel source so VibeDraft stops reading from it.
top_postsYour best-performing posts, optionally per weekday — real exemplars to draft from, not a description of your style.
engagement_by_local_hourYour average engagement by hour of day, so an agent can place a post where your account actually performs.
engagement_by_dow_and_hourThe same, broken down per day of the week, with thin days flagged rather than guessed at.
list_scheduledEverything queued and not yet posted, each with a link that opens it in VibeDraft.
create_mediaRegister an image, GIF, or mp4 and get a signed URL to PUT the bytes to. Returns a media_id for preview_post or schedule_post, which verify the upload — there is no separate complete step. Needs a shell or HTTP client to upload the bytes; video must carry an audio track.
list_mediaMedia already uploaded and verified, newest first. The normal way to attach a file the user already has — no bytes move.
upload_mediaSend a TINY file's bytes inline (base64) when no network upload is possible — icons and cards, never video. You must generate every character yourself, so anything sizeable fails partway. Verifies on arrival.
import_mediaFetch a file from a public https link into the media library and verify it; returns a media_id. The route for a chat client that can only say where the file is. Nothing is posted.
get_capabilitiesThe formats an account accepts (X: post, thread; Instagram: image, carousel, reel, story; TikTok: video, photo; Threads: threads_post, threads_chain) with the exact media, caption and pricing rules for each, read from the channel adapter. Nothing is written.
preview_postValidate a draft, X text or an Instagram, TikTok or Threads payload, and get its exact cost plus a short-lived confirmation nonce. For TikTok the reply also carries the creator name, the privacy options and the consent line to show the user. Media is checked against the channel's rules here, with the fix named when it fails. Pass sources to back a draft's numbers and links: every number must be in a quote and every URL is fetched live, so a removed post or an unsourced figure refuses the preview instead of shipping. Nothing is published by this call.
publish_postPost immediately. Requires the nonce from preview_post, and publishes the content stored on that preview — not content passed here — to the channel it was previewed for.
publish_instagramPublish a previewed Instagram image, carousel, reel or story now, by preview_id. Instagram processes reels before they appear; the reply says 'processing' when that outlasts the call and VibeDraft finishes it. Refunded if Instagram rejects it.
publish_tiktokPublish a previewed TikTok video or photo post now, by preview_id. The video is uploaded to TikTok in chunks and processed before it appears; the reply says 'processing' when that outlasts the call and VibeDraft finishes it. Refunded if TikTok rejects it.
publish_threadsPublish a previewed Threads post or chain now, by preview_id. A chain goes out post by post, each a reply to the one before; video and carousels are processed before they appear, and the reply says 'processing' when that outlasts the call. Refunded if Threads rejects it.
cross_postQueue drafts across accounts and channels for one scheduled_for in a single call. Every draft is validated before anything is charged; charged per draft at that channel's price. Pass sources on a draft to back its numbers and links, same as preview_post. No preview, so show the user each draft first. Two or more drafts become one send, tracked together by get_send. A draft's lead_magnet_id marks it as offering that free resource.
group_postsGroup posts that went out separately on different channels into one send, so get_send can compare them. Posts must be yours and not already in a send. Posts nothing.
get_sendOne send: every message's channel, handle, status and link, its latest views, likes, replies and reposts, the totals, and which channel did best by views. A missing number is shown as missing, never 0.
get_nightly_settingsWhether vibedraft drafts tomorrow's posts each night for a handle: on or off, the hour and time zone it runs in, posts per day, quiet hours, warnings, and the last three nights with a link to review each. Every nightly post is a draft you approve.
list_formatsYour declared post formats per account, what each needs, and which facts are still missing before they can be drafted.
add_formatDeclare a post format (a named shape, optionally on set weekdays), or ban a shape you never want drafted.
update_formatChange a declared format's name, shape, channel or weekdays.
set_format_statePin, ban or reactivate a declared format.
set_format_factGive an event format (revenue update, launch, milestone) the real fact for its date; without one it is not drafted.
remove_formatDelete a declared format.
set_nightly_settingsTurn nightly drafts on or off for a handle and set posts per day (one number or per weekday), the run hour, quiet hours, time zone (the handle's, or the login's own), and the switches for experiments and media. Off by default. Nothing posts without your approval.
set_live_eventsSwitch pushed events on or off for one of your accounts. On X: likes, replies, quotes, reposts, mentions, follows and unfollows as they happen, shown per message in get_send. X bills each event (about $0.005) under the monthly X budget, and events pause when it is spent.
set_reply_assistantSet reply drafts for a handle: off, review (a reply in the handle's voice waits for you after someone replies to its X posts, comments on its Instagram posts or replies on Threads; you send, edit or skip it), or auto (X only: plain reactions also go out on their own after a 2 to 25 minute hold you can cancel; asks you to confirm first, and only once the handle's drafts have proven themselves). Instagram and Threads need the read-replies grant.
list_reply_draftsReplies drafted in your voice for people who replied to your X posts, commented on your Instagram posts or replied on Threads, waiting for you: the channel, who said what, the draft, a link, and (X) whether they look worth following. Also resource requests: answers drafted for people who asked for a lead magnet, a DM on Instagram or a public reply on Threads, each with their own link; filter by kind or lead_magnet_id.
send_reply_draftPost a drafted reply as you approved it, on its own channel (X, Threads or Instagram) from the handle it was drafted for; on X only, optionally also like their reply and follow them. A DM draft is approved, not sent: you get the exact text and the Instagram conversation link to send it yourself.
mark_reply_draft_sentAfter you sent an approved DM draft (a resource request on Instagram) yourself, mark it sent; it goes on the lead's timeline. Needs confirm_sent: true.
skip_reply_draftDismiss a drafted reply without posting anything; their reply stays in your Inbox.
list_sendsYour recent sends, newest first, with each one's status and channels.
preview_articleConvert markdown into a long-form X Article and check it before anything is created. Returns a one-time preview_id. Nothing is sent to X by this call.
publish_articleRedeem an article preview. Creates a private draft you can still read and discard on X, and only publishes publicly when the preview was made with publish: true. Requires X Premium.
list_blog_targetsList the blogs a draft can be rendered for. Each has its own frontmatter schema and body dialect.
preview_blogValidate a blog draft against a target's schema and convert its body to that target's dialect. Returns a one-time preview_id. Publishes nothing.
publish_blogRedeem a blog preview. The default export mode returns the rendered file and writes nothing. Mode git commits it to your blog repo and opens a pull request it never merges. Mode api publishes to dev.to, Hashnode or Ghost, as a draft unless the preview said publish live. Free in every mode.
schedule_articleSchedule an X Article: the private draft is created now so you can read it in X, and it goes public at the time you pick.
schedule_blogSchedule a blog post or newsletter issue on your GitHub-backed site: the PR opens now for review and is merged at the time you pick.
syndicate_blogPackage an already-published post for Medium or Substack: paste-ready body plus a checklist. Free — those platforms have no publishing API, so the human pastes.
schedule_postQueue a post, thread, or Instagram / TikTok / Threads payload for later and get the link that opens it in VibeDraft. Pass sources to back its numbers and links, same as preview_post. lead_magnet_id marks a post that offers a free resource, so replies asking for it become leads. Credits are debited atomically with the write.
cancel_scheduledRemove something from the queue before it goes out.
approve_postsApprove drafts so they publish at their times: by id, every draft on the account, or the ones whose time passed. A draft whose time passed moves to its next open slot first.
unapprove_postsTake approved posts out of the publishing queue and keep them as drafts.
edit_scheduled_postChange a queued post's text, its time, or the lead magnet it offers (any status, null clears it). A thread moves together.
delete_postsPermanently delete drafts, pending or failed posts, whole threads included. Cannot be undone.
regenerate_postRewrite one queued post in its account's voice. Status and time stay as they were.
retry_postPut a failed post back in the queue, moved a few minutes out if its time has passed.
emit_eventRecord something that happened to the product — a milestone crossed, a feature shipped, a customer won or churned, a theme in support — so it can be marketed later. Writes to VibeDraft only: nothing posts, nothing is scheduled, no credits are spent. A plain-words title is required.
Machine-readable pricing lives at /pricing.md.
Attaching a file you already have.
A hosted tool call carries text, so a file has to be typed out as base64 to ride inside one. upload_media exists for that and stops at 64 KB, which is an icon. A screenshot is usually 150-250 KB and a video is megabytes, so for anything real the bytes have to move some other way.
The way that works everywhere: register with create_media and PUT the bytes yourself. Any shell, any HTTP client, any language. preview_post then verifies the upload and shows you a link to watch or view it before anything publishes.
curl -X POST "https://vibedraft.app/api/v1/media/<id>/upload" \
-H "Authorization: Bearer vd_pat_..." \
-H "Content-Type: image/png" --data-binary @shot.pngNo terminal? Install the one-click extension instead. Double-click it, paste the same token, and your agent can attach any file on your machine without you touching a command line. Claude Desktop only, and it cannot work on a phone: a remote server has no filesystem to read, so mobile attaches from list_media instead.
Nothing posts without you.
Every publish path requires a confirmation step. Your agent calls preview_post first, which validates the draft, quotes its exact credit cost and returns a short-lived nonce. Publishing sends the text stored on that preview, not text supplied afterwards. An agent cannot draft something, change it, and post it in one move.