For developers

The Welder MCP server

Welder is a remote MCP server at https://weldergtm.com/mcp. Call get_context first, publish with create_post, and hand files over with create_upload (a shell) or create_upload_link (a chat app). Technical words live on this page only; the rest of the site speaks plainly.

Endpointhttps://weldergtm.com/mcp

Endpoint#

POST https://weldergtm.com/mcp
  • Streamable HTTP, stateless: no session id is needed. Protocol versions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
  • Answers are JSON; with Accept: text/event-stream you may get a stream carrying exactly one response.
  • Requests up to 1 MiB, batches of up to 20. GET and DELETE on /mcp answer 405.
  • Every tool returns structured content plus a short text version the model can read aloud, and advertises a title and the four MCP annotations (read-only, destructive, open world, idempotent).

Authentication and scopes#

OAuth 2.1 (preferred). Clients discover everything themselves: an unauthenticated call gets 401 with a pointer to the protected resource metadata, clients register dynamically, and sign-in happens in the browser with PKCE. Access tokens last an hour and refresh for 90 days.

Discovery
https://weldergtm.com/.well-known/oauth-protected-resource
https://weldergtm.com/.well-known/oauth-authorization-server

Scopes. Every tool requires sign-in and declares the scope it needs in securitySchemes. read covers lookups inside the workspace, validation and stored numbers (9 tools); get_post may also ask TikTok for a pending link and store it, so it is not marked read-only. publish covers uploads, account connection links, publishing, cancelling, deleting and dismissing warnings, and every call that can change state or spend credits (12 tools): waiting on an upload link, refreshing numbers and reading replies are among them. A credential without the scope gets forbidden.

API keys. For clients without OAuth, scripts and CI. Create one under Advanced → Keys and send it as a bearer token. Keys start with wk_live_, are shown once and carry both scopes.

curl -X POST https://weldergtm.com/mcp \
  -H "Authorization: Bearer {key}" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

A credential belongs to exactly one workspace, so there is no workspace header to send.

Install in a coding app#

Each app has a friendly guide with the exact command or config. The short version:

Claude CodeGuide

claude mcp add --transport http welder https://weldergtm.com/mcp

Codex CLIGuide

codex mcp add welder --url https://weldergtm.com/mcp

CursorGuide

.cursor/mcp.json
{
  "mcpServers": {
    "welder": { "url": "https://weldergtm.com/mcp" }
  }
}

WindsurfGuide

~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "welder": { "serverUrl": "https://weldergtm.com/mcp" }
  }
}

Gemini CLIGuide

gemini mcp add --transport http welder https://weldergtm.com/mcp

VS Code (Copilot)Guide

code --add-mcp '{"name":"welder","type":"http","url":"https://weldergtm.com/mcp"}'

ClineGuide

Open Cline → MCP Servers → Remote Servers and add a server named welder with the URL https://weldergtm.com/mcp.

Any client or scriptGuide

https://weldergtm.com/mcp

Or let the agent install itself:

Paste into your agent
Install the Welder MCP server for this client the native way (HTTP transport, URL https://weldergtm.com/mcp). Then call the `get_context` tool and tell me which accounts are connected. Never print credentials back to me.

The Welder plugin#

Welder: TikTok, Instagram & YouTube Autoposting packages five ready-made workflows with a connection to this server: get started and connect, publish content, schedule content, review performance and engage your audience. The package holds readable instructions, connection settings and brand images. It installs no program, background process or hook, and it carries no credentials: you sign in with Welder in your browser, through your app’s own sign-in.

Claude Code. Add the marketplace and install the plugin, one line at a time, inside Claude Code. Then run /mcp, pick the Welder server and choose Authenticate; your browser opens to sign in.

In Claude Code
/plugin marketplace add smvls/welder-plugins
/plugin install welder-social-manager@welder-plugins

Codex. Codex CLI installs the same package from the public repository. Sign in with Welder through Codex’s own sign-in flow; your browser opens to approve access.

Plugin, in a terminal
codex plugin marketplace add smvls/welder-plugins
codex plugin add welder-social-manager@welder-plugins

Prefer the server on its own, without the workflows? Add it and sign in once:

Server only, in a terminal
codex mcp add welder --url https://weldergtm.com/mcp
codex mcp login welder

Claude and ChatGPT apps. Add https://weldergtm.com/mcp as a custom connector in their settings and sign in; the Claude guide and the ChatGPT guide show each screen.

Directory listings. The Claude and ChatGPT plugin directories each review and approve a plugin before listing it. Meanwhile, install from the public repository or connect Welder directly.

Tools#

Workspace and accounts

Who is connected, what they can post, and how to connect another network.

get_connected_profile

read

Get connected Welder profile. Annotations: Read-only, Idempotent

Identifies the Welder workspace this credential belongs to, so a host that supports several accounts can tell them apart.

No input.

Returns

{ id }

  • id is a stable, opaque workspace id resolved only from the validated credential. It never contains an email, the workspace name or a credential.
  • Marked as the authenticated profile tool (_meta["openai/profile"]: true). The text content carries the same JSON as the structured result.
Call
get_connected_profile({})
Result (example)
{ "id": "7c1e2a9d-…" }

get_context

read

Get Welder publishing context. Annotations: Read-only, Idempotent

Start here. The workspace, whether publishing is open right now, this month’s allowance and usage, connected accounts, what each network accepts, and how to hand over files.

No input.

Returns

{ workspace: { id, name, plan_status, limits: { posts, x_credits }, usage: { posts, x_credits }, can_publish }, accounts: Account[], capabilities: { tiktok, instagram, youtube, threads, bluesky, x }, media_hint, links: { app, connect_accounts }, server: { version }, next_step? }

  • When can_publish is false, next_step says so: “Publishing is unavailable under this account's current entitlement. Account setup, uploads and validation remain available.” Subscriptions are managed on the Welder website, never through an assistant.
  • capabilities is keyed by network, with the same limits as get_capabilities.
  • In the simulated review workspace the result also carries review_demo: true and a data_source line, and media_hint explains the sample media. See Review workspace below.
Call
get_context({})
Result (example)
{
  "workspace": {
    "id": "7c1e2a9d-…",
    "name": "Nightshift Studio",
    "plan_status": "active",
    "limits": { "posts": 1500, "x_credits": 100 },
    "usage": { "posts": 3, "x_credits": 8 },
    "can_publish": true
  },
  "accounts": [{ "id": "b2d4…", "provider": "x", "username": "nightshiftHQ", "status": "active", … }],
  "capabilities": { "tiktok": { … }, "instagram": { … }, … },
  "media_hint": "In chat apps without a shell, use create_upload_link and show its URL to the person; then get_upload_link for media ids. With a shell, use create_upload and finalize_upload.",
  "links": {
    "app": "https://weldergtm.com/app",
    "connect_accounts": "https://weldergtm.com/app/accounts"
  },
  "server": { "version": "0.1.0" }
}

list_accounts

read

List connected social accounts. Annotations: Read-only, Idempotent

Connected accounts with their status and what each can publish. Use the ids as create_post targets.

No input.

Returns

{ accounts: [{ id, provider, username, display_name, avatar_url, profile_url, status: 'active' | 'reauth_required' | 'disconnected', capabilities, connected_at, expires_at, defaults, last_error }] }

  • defaults holds provider facts the agent may need, such as the TikTok privacy levels an account offers or the Instagram account type.

connect_account

publish

Connect a social account. Annotations: Changes data

A short-lived link (10 minutes) that opens the connect flow for a network in the person’s browser. Show it, then call list_accounts once they finish.

InputDescription
providerrequired'tiktok' | 'instagram' | 'youtube' | 'threads' | 'bluesky' | 'x'The network to connect.

Returns

{ provider, url, expires_in }

  • The person signs in on the network’s own screen; the assistant never sees a password. For Bluesky the link opens the app-password form.
  • Refused with forbidden in the simulated review workspace, whose sample accounts are provisioned by Welder.

Files

Getting a photo or video into Welder: an upload link for chat apps, a signed upload for shells, or a public URL.

list_media

read

List uploaded media. Annotations: Read-only, Idempotent

Recent uploads, newest first, for requests such as “post my latest upload”. Expired files are left out.

InputDescription
limit1–50Default 10.
status'ready' | 'pending'Filter by status.

Returns

{ media: [{ id, kind, mime, bytes, width, height, duration_ms, filename, status, error, preview_url: null, expires_at, created_at }] }

create_upload

publish

Create a media upload. Annotations: Changes data

For agents with a shell: reserves a media slot and returns a signed upload URL plus the exact curl command to PUT a local file.

InputDescription
filenamerequiredstringOriginal file name, e.g. launch.mp4.
mimerequired'image/jpeg' | 'image/png' | 'image/webp' | 'video/mp4' | 'video/quicktime'File type.
bytesrequiredintegerFile size in bytes. Images up to 30 MB, videos up to 500 MB.

Returns

{ media_id, upload: { method: 'PUT', url, headers }, expires_in, curl }

  • The upload URL is valid for 2 hours and is a secret. Original media is kept for 7 days; each media post keeps one small image in its history.
Call
curl -sS -X PUT -H "Content-Type: video/mp4" --upload-file "launch.mp4" "<upload url>"

finalize_upload

publish

Finalize a media upload. Annotations: Changes data, Idempotent

Checks the uploaded object (size, type, and dimensions and duration when possible) and marks it ready. Calling it again on a ready file returns the same media.

InputDescription
media_idrequiredstring (uuid)From create_upload.

Returns

{ media: Media }

  • If the file is not there yet the result is media_not_ready, which is safe to retry shortly.

import_media

publish

Import media from a public URL. Annotations: Changes data, Open world

Fetches a public https file into Welder’s storage instead of uploading from disk.

InputDescription
urlrequiredstring (https)A public file URL. Private networks and more than three redirects are refused.
filenamestringOptional file name.

Returns

{ media: Media }

  • The simulated review workspace imports only Welder’s public sample files; any file can still be uploaded the normal way.

Posts

Check, publish, schedule, follow, cancel, take down and dismiss warnings.

validate_post

read

Validate a social post. Annotations: Read-only, Idempotent

A dry run of create_post: publishing access and allowance, accounts, media per network, caption lengths, image counts and known limits. Nothing is posted and nothing is used up.

InputDescription
…same as create_postPass exactly what you would pass to create_post.

Returns

{ ok, issues, targets: [{ account_id, provider, username, ok, issues, resolved }], cost: { posts, x_credits } }

  • Issues with code warning are advice, for example that Instagram shows no caption on Stories. They never block publishing.

create_post

publish

Publish or schedule a social post. Annotations: Changes data, Destructive, Open world, Idempotent

Publishes one caption and media set to one or more accounts, now or at schedule_at. Returns at once with the post, or waits up to wait_seconds for the result.

InputDescription
captionrequiredstringCaption, description or post text, depending on the network.
targetsrequired[{ account_id } | { provider }]A provider shorthand targets every active account of that network.
mediastring[]Media ids. Leave empty for a text post (Threads, Bluesky, X).
titlestringYouTube title or TikTok photo title; derived from the caption when absent.
idempotency_keystringRecommended: one stable key per post, reused on every retry. Without one, identical inputs are still deduplicated automatically.
schedule_atISO 8601Publish later, up to 30 days ahead.
optionsobjectPer-network options: tiktok, youtube, instagram, threads, bluesky, x (Stories, drafts, replies, quotes, polls, alt text and more). See Options per network below.
wait_seconds0–50Block until every target settles or the time runs out, then return the current state.

Returns

{ post: { id, status, source, client, caption, title, media, media_count, targets: [{ id, account_id, provider, username, status, attempt, url, platform_post_id, error_code, error_message, ambiguous, published_at, updated_at, kind }], scheduled_at, created_at, updated_at, completed_at } }

  • A request that names the exact caption, accounts and time already approves the post; no second confirmation is needed. Ask only about a missing or conflicting detail, such as no caption, an account name that matches two profiles or two different times: a published post can’t always be taken back.
  • TikTok privacy and YouTube visibility default to the workspace settings (public unless the person changes them). The AI-generated label is on by default for TikTok and YouTube.
  • options.instagram.kind = "story" posts an Instagram Story; a reply option (threads.reply_to_id, x.reply_to_post_id, bluesky.reply_to) answers a comment from list_comments.
  • If a target comes back ambiguous, the network may have published it: check the account instead of posting again with a new key.
Call
create_post({
  idempotency_key: "cold-brew-launch",
  caption: "Cold brew season is open. Twelve hours, zero heat.",
  media: ["3ed1…"],
  targets: [{ provider: "tiktok" }, { provider: "youtube" }],
  wait_seconds: 30
})

get_post

read

Get social post status. Annotations: Changes data, Open world, Idempotent

The current state of a post and each of its targets, with the live link once a network confirms it.

InputDescription
post_idrequiredstring (uuid)From create_post.
wait_seconds0–50Wait for targets to settle, and for a pending TikTok link, before answering. Default 0.

Returns

{ post: Post }

  • needs_attention is true while a failed or partly published post has a warning nobody dismissed; attention_dismissed_at says when one was dismissed.
  • A TikTok target with url_pending has been posted, but TikTok hasn’t supplied its public link yet (it can still be in moderation). Call again later; a private post may never get a public link, and none is ever made up.
  • For a pending TikTok link, get_post may ask TikTok for that post’s status and store the link once it exists. That is the only change it can make: it never publishes, retries or changes usage, so it needs only the read scope. The provider read makes it open-world, and storing the link means it is not read-only. The simulated review workspace never asks TikTok.

list_posts

read

List social posts. Annotations: Read-only, Idempotent

Recent and scheduled posts, newest first.

InputDescription
limit1–50Default 20.
cursorstringFrom next_cursor of the previous page.
status'queued' | 'publishing' | 'published' | 'partial' | 'failed' | 'canceled' | 'needs_attention'Only posts in this state. needs_attention lists failed and partly published posts whose warning hasn’t been dismissed.

Returns

{ posts: Post[], next_cursor }

dismiss_post_attention

publish

Dismiss a post warning. Annotations: Changes data, Idempotent

Dismisses the warning on a failed or partly published post. The post keeps its results in history and leaves the needs_attention list.

InputDescription
post_idrequiredstring (uuid)The post whose warning to dismiss.

Returns

{ post: Post }

  • Does not retry, publish, delete or cancel anything, and contacts no network. The original errors, media and usage stay as they were.
  • Safe to call again. A post without an unresolved warning answers conflict. A later explicit retry clears the dismissal, so new failures show up again.
Call
dismiss_post_attention({ post_id: "8c1f…" })

cancel_post

publish

Cancel a scheduled social post. Annotations: Changes data, Destructive, Idempotent

Stops queued and scheduled targets before they go out. Targets already publishing finish.

InputDescription
post_idrequiredstring (uuid)The post to cancel.

Returns

{ post: Post }

  • Cancelling can’t be undone; schedule the post again to bring it back.

delete_post

publish

Delete a published social post. Annotations: Changes data, Destructive, Open world, Idempotent

Takes a published post down. Removes it from X and Bluesky; for the other networks the result says to delete it in their app. It can’t be undone: a clear request to delete that post is the go-ahead, and an unclear post or account is worth one question first.

InputDescription
post_idrequiredstring (uuid)The post to take down.
account_idsstring[]Only these accounts’ copies. Default every published copy.

Returns

{ post: Post, results: [{ target_id, account_id, provider, result: 'deleted' | 'already_deleted' | 'unsupported' | 'not_published' | 'failed', message }] }

  • A removed copy keeps the status deleted on the post, and its numbers stop updating. Deleting never gives back used posts or X credits.
  • Refused with forbidden in the simulated review workspace: its posts never reached a network.
Call
delete_post({ post_id: "8c1f…", account_ids: ["e7a9…"] })

Results and reference

Numbers, replies and what each network accepts.

get_analytics

read

Get social analytics. Annotations: Read-only, Idempotent

How posts are doing from stored numbers: views, likes, comments, shares and followers per network and account, plus the top posts. A missing number means the network doesn’t share it; the note says why.

InputDescription
days1–365Posts published in the last N days. Ignored when since is set.
since, untilISO 8601A custom period, by publish time.
provider'tiktok' | 'instagram' | 'youtube' | 'threads' | 'bluesky' | 'x'Only one network.
account_idstring (uuid)Only one account.
sort'published_at' | 'views' | 'likes' | 'comments' | 'shares' | 'interactions' | 'engagement_rate'Order of top_posts. Default published_at.
limit1–50How many top posts. Default 20.

Returns

{ period: { since, until }, as_of, posts, measured, totals, interactions, engagement_rate, by_provider: [{ provider, posts, measured, totals, note }], accounts: [{ account_id, provider, username, status, note, metrics, change, captured_at, history }], top_posts: TargetAnalytics[] }

  • The period picks posts by publish time; every number is that post’s latest total, not growth inside the period.
  • Counters: views, reach, likes, comments, shares, reposts, quotes, saves, link_clicks, profile_visits, follows, engagements, video_views, watch_time_s, avg_watch_time_s. A counter the network does not report is absent, never 0.
  • status, per post and account: available, pending, permission_required, unsupported, unavailable or error.
Call
get_analytics({ days: 28, sort: "views", limit: 5 })

get_post_analytics

publish

Get or refresh post analytics. Annotations: Changes data, Open world

One post’s numbers on each network, with the reason when some are missing. refresh asks the networks now; history shows how the numbers grew.

InputDescription
post_idrequiredstring (uuid)The post.
refreshbooleanCollect fresh numbers now instead of waiting for the schedule (at most once every 10 minutes per post). Default false.
historybooleanAdd every collected point, oldest first. Default false.

Returns

{ post_id, caption, created_at, targets: [{ target_id, account_id, provider, username, url, status, note, metrics, extra, interactions, engagement_rate, captured_at, next_refresh_at, history? }], totals, as_of }

  • Needs the publish scope because refresh: true reads the networks and stores the new numbers; those provider reads make it open-world.
  • Numbers are collected about 1 hour, 6 hours, 1 day, 3 days, 7 days, 30 days and 90 days after publishing (Instagram Stories: 1, 6 and 20 hours; X: 1 hour, 1, 7, 29 and 90 days).
  • Refreshing too often returns rate_limited with details.retry_after_seconds. In the simulated review workspace refresh is ignored and the fixed sample numbers are returned.
Call
get_post_analytics({ post_id: "8c1f…", refresh: true, history: true })

list_comments

publish

Read comments on a post. Annotations: Changes data, Open world

Replies under a published post, network by network. To answer one, call create_post with that account as the target and the comment id in the option named by reply_option.

InputDescription
post_idrequiredstring (uuid)The post.
account_idstring (uuid)Only this account’s copy. Default every published copy.
limit1–50Default 20.

Returns

{ post_id, targets: [{ target_id, account_id, provider, username, status, note, reply_option, comments: [{ id, author: { username, display_name, avatar_url }, text, created_at, likes, replies, url, parent_id, is_own, hidden }] }] }

  • Needs the publish scope: each read of X replies uses 1 X credit, and a network read can refresh the account’s sign-in. Reading comments from the networks makes it open-world.
  • Readable today: Bluesky, YouTube (read only), X with the account’s X entitlement (the last 7 days), and Threads where the connection holds the replies permission. Instagram answers permission_required and TikTok unsupported.
  • reply_option is threads.reply_to_id, x.reply_to_post_id or bluesky.reply_to; null where Welder cannot reply (YouTube). Send an answer when the person asks for it with its words; when they ask for a draft, write it in the chat and send nothing.
Call
list_comments({ post_id: "8c1f…", account_id: "b2d4…" })
create_post({
  caption: "Not yet, but it’s on the list!",
  targets: [{ account_id: "b2d4…" }],
  options: { threads: { reply_to_id: "17890…" } }
})

get_capabilities

read

Get social network capabilities. Annotations: Read-only, Idempotent

What every network accepts: media types, sizes, durations, image counts and caption limits. Static, the same for every workspace.

No input.

Returns

{ providers: { tiktok: { text_only, video: { max_bytes, min_duration_s, max_duration_s, mimes }, images: { max_bytes, min_count, max_count, mimes }, mixed_carousel, caption_max, title_max, story?, notes }, … } }

Options per network#

Pass these under options.<network> in create_post and validate_post. Combinations a network refuses (a poll with media, a caption on a Story) come back from validate_post as issues; advice comes back as warning and never blocks publishing.

TikTokoptions.tiktok

OptionWhat it does
privacypublic · friends · followers · privateWho can see the post. Defaults to your workspace setting.
ai_generatedtrue · falseAdds TikTok’s AI-generated content label. Default true.
disable_comment, disable_duet, disable_stitchtrue · falseTurn interactions off for this post.
brand_content, brand_organictrue · falseDisclose paid partnerships or your own brand promotion.
modedirect · draftdraft sends the video or photos to the creator’s TikTok inbox to finish in the app. Default direct.
cover_index0–34Photo posts only: which image is the cover.
cover_timestamp_msmillisecondsVideos only: the frame TikTok shows as the cover.
auto_add_musictrue · falsePhoto posts only: let TikTok add recommended music. Default true.

Instagramoptions.instagram

OptionWhat it does
kindreel · image · carousel · storyOverride the inferred post type. A Story is never inferred; Meta allows Stories from Business accounts.
share_to_feedtrue · falseReels only: also show the Reel in the feed. Default true.
cover_media_idmedia idReels only: an uploaded image to use as the cover (not part of media).
thumb_offset_msmillisecondsReels only: the frame used as the cover when no cover image is given.
collaboratorsup to 3 usernamesInvite collaborators on a feed post or Reel (not Stories).
user_tags[{ username, x?, y?, media_index? }], up to 20Tag people. x and y (0 to 1) place a tag on an image; Reels and Stories take usernames only.
location_idlocation idAdd a location to a feed post or Reel.
alt_textsup to 10 stringsAlt text per image, in media order (feed images).
audio_nameup to 100 charactersReels only: the name shown for the original audio.
trial{ graduation: manual · performance }Reels only: a trial Reel is shown to non-followers first; graduation decides when followers see it.

YouTubeoptions.youtube

OptionWhat it does
visibilitypublic · unlisted · privateWho can watch. Defaults to your workspace setting.
titleup to 100 charactersTaken from the caption when you leave it out.
tagsstring listSearch tags for the video.
made_for_kidstrue · falseRequired disclosure for content made for children.
ai_generatedtrue · falseSynthetic media disclosure. Default true.
thumbnail_media_idmedia idAn uploaded JPEG or PNG up to 2 MB, used as the custom thumbnail (not part of media).
category_idYouTube category idThe video’s category, e.g. 22 for People & Blogs.
publish_atISO 8601 timeYouTube makes the video public at this time; until then it stays private.
notify_subscriberstrue · falseTell subscribers about the upload. Default true.
licenseyoutube · creative_commonsThe video’s license.
embeddabletrue · falseAllow other sites to embed the video.
public_stats_viewabletrue · falseShow the view count on the watch page.
default_languagelanguage code, e.g. enLanguage of the title and description.
default_audio_languagelanguage codeLanguage spoken in the video.
recording_dateISO 8601 timeWhen the video was recorded.

Threadsoptions.threads

OptionWhat it does
reply_linkURLPosted as the first reply under your post.
reply_controleveryone · accounts_you_follow · mentioned_only · parent_post_author_only · followers_onlyWho can reply. Default everyone.
topic_tagone tag, without #A topic tag for the post (no periods or ampersands).
reply_to_idThreads post idPublish as a reply to this post: build a thread, or answer someone.
quote_post_idThreads post idQuote this post.
link_attachmentURLText posts only: a link preview card.
poll{ options: 2 to 4, up to 25 characters each }Text posts only: a poll.
spoilertrue · falseBlur the photos and videos until tapped.
alt_textsup to 20 stringsAlt text per image or video, in media order.
reply_approvalstrue · falseHold replies for your approval before they show.

Blueskyoptions.bluesky

OptionWhat it does
alt_textsup to 4 stringsAlt text for each image, in order.
langsup to 3 language codes, e.g. enLanguages of the text. Default en.
labelssexual · nudity · porn · graphic-mediaContent warnings shown before the media.
reply_controlnobody, or any of mentioned · following · followersWho can reply. Default everyone.
disable_quotestrue · falseStop other people from quoting the post.
reply_toat:// URI or bsky.app linkReply to this post: build a thread, or answer someone.
quoteat:// URI or bsky.app linkQuote this post (with or without your own images or video).

Xoptions.x

OptionWhat it does
alt_textsup to 4 stringsAlt text for each image, in order.
reply_settingsfollowing · mentionedUsers · subscribers · verifiedWho can reply. Default everyone.
reply_to_post_idX post idReply to this post: build a thread, or answer someone.
quote_post_idX post idQuote this post.
poll{ options: 2 to 4, duration_minutes: 5 to 10080 }Text posts only: a poll, open 5 minutes to 7 days.
made_with_aitrue · falseDisclose AI-generated media.
paid_partnershiptrue · falseDisclose a paid partnership.
community_idX community idPost into an X Community the account belongs to.

Numbers and replies#

get_analytics and get_post_analytics read the numbers Welder collects on a schedule after each post goes live; list_comments reads replies. What a network shares depends on the permissions it granted Welder:

  • TikTok: None yet. TikTok hasn’t approved stats for Welder, so views, likes and followers stay in the TikTok app for now.
  • Instagram: Likes and comments on each post, and your follower count. Views, reach, saves and Story views need a permission Instagram hasn’t given Welder yet.
  • YouTube: Public views, likes and comments on each video, plus subscribers. Nothing to set up.
  • Threads: Views, likes, replies, reposts, quotes and shares on each post, plus followers. Threads is still reviewing Welder, so until it approves, it may ask for a permission instead.
  • Bluesky: Likes, reposts, replies, quotes and bookmarks on each post, plus followers. Bluesky doesn’t count views.
  • X: Views, likes, replies, reposts, quotes and bookmarks on each post, plus followers; link clicks and profile visits too, for a post’s first 30 days.

A counter a network does not report is absent, never 0, and each post or account carries a status (available, pending, permission_required, unsupported, unavailable, error) with a plain note. Welder deletes posts only on X and Bluesky (delete_post); elsewhere the result tells the person to delete it in the app.

Files from chat apps#

An assistant without a shell calls create_upload_link, shows the link, waits for the person to drop their files on the Welder upload page, then calls get_upload_link with wait_seconds and passes the media ids to create_post. A shell-capable agent uses create_upload, one PUT and finalize_upload. list_media finds what a person uploaded in the app.

Errors#

Domain failures come back as tool results with isError: true, never as protocol errors, so the model can read them and act. Messages never contain URLs, credentials or email addresses.

{ "error": { "code": "quota_exceeded", "message": "Monthly post quota exceeded.", "retryable": false } }
CodeMeaning
unauthorizedMissing, expired or revoked credentials.Retry: No: sign in again or use a valid key.
forbiddenThe credential lacks the scope this tool needs, or the action isn’t allowed for this workspace (account changes in the simulated review workspace, for example).Retry: No
validationThe input does not match the tool schema.Retry: No: fix the input.
not_foundThe post, media, upload link or account does not exist in this workspace.Retry: No
quota_exceededA limit was reached: this month’s posts or X credits, connected profiles, unfinished uploads or media storage. The message says which.Retry: No
plan_requiredPublishing, or X publishing, is unavailable under the account’s current entitlement. Setup, uploads and validation still work.Retry: No
account_not_connectedNo active account for the requested network.Retry: No: call connect_account.
account_reauth_requiredThe network asked the person to sign in again.Retry: No: call connect_account for a fresh link.
media_not_readyThe upload has not arrived or is still being checked.Retry: Yes, shortly.
media_too_largeThe file is over the size limit.Retry: No
unsupportedThe network does not accept this media or combination.Retry: No
provider_errorThe network rejected the request; details.provider_code has its reason.Retry: Depends on the reason.
rate_limitedToo many calls. details.retry_after_seconds says when to try again.Retry: Yes, after the wait.
conflictThe request conflicts with the current state (for example, cancelling a finished post or dismissing a warning that isn’t there).Retry: No
closedThe upload link expired or was already completed.Retry: No: make a new link.
internalSomething failed on our side.Retry: Yes

Rate limits#

  • Per workspace: 60 tool calls a minute and 600 an hour.
  • create_post, create_upload and create_upload_link: 30 a minute each. Refreshing numbers: 10 a minute.
  • Unauthenticated OAuth endpoints and the public upload-link routes: 30 to 60 a minute per IP.
  • Over the limit you get rate_limited with details.retry_after_seconds, or HTTP 429 with Retry-After outside tool calls.

Prompts and resources#

  • Prompt post_this walks an agent through upload, validation and publishing for a file and a list of networks.
  • Resource welder://capabilities is the same data as get_capabilities; welder://docs/quickstart is a short guide in markdown, so agents can read the docs without a browser.

Review workspace#

Plugin directory reviewers get one dedicated Welder workspace, labelled Simulated review workspace on every page of the app. It runs the same tools and checks as every workspace, but its social side is simulated: no social network is ever contacted. If you followed a post link from that workspace, this is where it lands.

Synthetic

  • Six sample accounts, one per network, named demo_coffee_… and shown as “Demo Coffee · Simulated”. They hold no real sign-in.
  • A sample coffee photo and three sample posts on the Instagram and Bluesky sample accounts, dated September 29 to October 1, 2026. Their numbers are fixed example values, not network results.
  • One fictional reply on the Bluesky copy of the October 1 post. Replies are drafted in the chat and never sent.

Works as usual

  • Uploads through upload links and the app, validation, allowances, idempotency, scheduling, cancelling and post history.
  • Publishing records the post and returns a simulated receipt for each account: it shows as published, and its link opens this page instead of a network. New posts get no invented numbers or replies.
  • Connecting your AI app: signing in, keys and removing access work as usual.

Turned off

  • Connecting, renewing or disconnecting social accounts.
  • Checkout, plan changes and payment settings.
  • Changing settings or deleting the workspace.
  • Taking posts down (delete_post) and refreshing numbers from the networks.
  • Importing files from anywhere other than Welder’s public sample files.

get_context returns review_demo: true and a data_source line for this workspace, and the app receives the same flag. Comments come back with a note that they are fictional samples. Every other workspace publishes to real networks and never sees simulated results.