For agents and developers

Mellow Hub API & MCP documentation

The same reference your agent reads. Plain text: /hub/llms.txt. MCP: https://www.mellow.world/mcp.

Start with the MCP publishing guide · Mellow Hub

whoami

What this credential is allowed to do: its mode (autopilot publishes; review only prepares), its scopes, the channels it can reach, how many posts it has left today, and whether the account's subscription allows publishing at all. Call this before anything else — it answers the questions you would otherwise discover by being refused.

list_platforms

Every network Mellow publishes to and the rules it enforces: caption limits, media counts and kinds, required fields, available placements. Read this instead of guessing a limit.

list_channels

The owner's connected accounts, with the channel IDs you name in a post. Pass refresh: true after a person has just connected something, to re-read from the provider rather than the local copy.

connect_channel

Returns a link a PERSON must open to connect a network. You cannot complete this yourself: every network asks a human to sign in, and Bluesky asks for a handle and an app password. Give the link and the instruction to the person, then call list_channels with refresh once they say they are done.

register_media

Checks that a public https URL can be fetched and reports what it is. Nothing is copied — the network fetches the URL when it publishes. If you hold bytes rather than a URL, call request_upload_url instead.

request_upload_url

A signed URL to PUT file bytes to, plus the media URL to use in a post afterwards. Use this for anything you hold as bytes, and for any video: the file goes straight to storage and never through Mellow.

validate_post

Checks a post against every channel it names and publishes NOTHING. Returns every problem at once, each naming the channel and the field. Call this before create_post every time — it costs nothing and it is the difference between fixing a post once and learning the rules by publishing badly.

preview_post

Renders what the post will look like on each network, without publishing. Use it when the question is how something will read rather than whether it is allowed; validate_post answers the second.

create_post

Publishes to every named channel, or schedules it with scheduledAt. THIS IS PUBLIC AND CANNOT BE UNDONE once a network has published. idempotencyKey is required: derive it from the content or the task, not at random, so a timeout you retry returns the original post instead of publishing a second one. Under a review-mode credential this prepares the post and stops for a person to approve.

get_post

The post and its per-channel outcome, with the public link where a network published. Read targets, not just status: partial means some channels succeeded and republishing everything would duplicate them.

list_posts

Recent posts, newest first, optionally filtered by status. Use it to find a post after a timeout.

cancel_post

Withdraws a post that has not gone out yet. A post a network has already published cannot be cancelled through any API — that has to be done in the network itself, by a person.

Resources mellow://guide/* — limits, errors and worked examples. Connect an agent

Mellow Hub

Publish one post to every network you are connected to — Instagram, TikTok, YouTube, X, LinkedIn, Threads, Bluesky, Pinterest and Facebook — through one API call, with each network's own rules checked before anything goes out.

Base URL: https://www.mellow.world/api/hub/v1 MCP endpoint: https://www.mellow.world/mcp

The shape of the work

  1. whoami — what this credential may do, which channels it can reach, and how many posts are left today. Call it first; everything else depends on the answer.
  2. list_channels — the connected accounts. Each has an id beginning spc_; that is what you name in channels.
  3. register_media — if the post has media. Any public https URL works and nothing is copied: the network fetches it when it publishes. For bytes you hold, ask for an upload URL and send the file straight there.
  4. validate_post — free, publishes nothing, and returns every problem at once. Do this before create_post every time. It is the difference between fixing a post once and discovering the rules by breaking them publicly.
  5. create_post — publishes now, or schedules for later.
  6. get_post — the result per channel, with the public link.

Connecting a channel

You cannot do this. Every network makes a person sign in, and Bluesky asks for a handle and an app password typed into Mellow. connect_channel returns a URL and a sentence explaining who has to open it — pass both on and wait. When the person is done, list_channels shows the new channel.

Writing one post for many networks

A post has one caption, one media list, and a list of channels. Where a network genuinely differs, say so in options, keyed by platform:

{
  "caption": "The same thought, everywhere.",
  "media": ["https://cdn.example.com/clip.mp4"],
  "channels": ["spc_instagram", "spc_youtube", "spc_bluesky"],
  "options": {
    "instagram": { "placement": "reels" },
    "youtube": { "title": "The same thought", "privacyStatus": "public" }
  },
  "perChannel": {
    "spc_bluesky": { "caption": "The same thought, in 300 characters." }
  }
}

options applies to every channel of that network. perChannel applies to one channel and wins over options — it is how you give Bluesky a shorter caption without shortening the post everywhere.

Publishing twice is the failure to avoid

create_post requires an idempotency key, and it is not decoration. If your request times out you do not know whether it published; retrying with the same key returns the original post instead of making a second one. Use a key derived from the content or the task, not a random value per attempt. Reusing a key for *different* content is refused, because that is a bug worth seeing.

Scheduling

Give scheduledAt an ISO 8601 timestamp to publish later; omit it to publish now. Cancelling works while a post is still scheduled. Once a network has published there is no unpublishing it through any API — Mellow will say so rather than pretend.

What comes back

Each channel succeeds or fails on its own. A post to five networks where YouTube rejects the title and the other four publish comes back as partial, with the failure named on the YouTube channel and public links on the rest. Read targets, not just status.

Authority and limits

A key is a delegation with edges: the channels it may touch, whether it publishes on its own (autopilot) or only prepares a post for a person to approve (review), and how many posts it may make in a day. whoami reports all of them. Everything done with the key is recorded in a journal the owner reads, and revoking a key takes effect immediately.

Reading further

  • mellow://guide/platforms — every network's limits and required fields.
  • mellow://guide/errors — what each refusal means and what to do about it.
  • mellow://guide/recipes — worked examples for the common jobs.

Every network, and what it asks for

These are the rules Mellow checks before it sends anything. A post that passes validate_post has already satisfied everything on this page; what remains is the network's own judgement about the file itself — resolution, duration, aspect ratio — which no API can answer in advance.

Instagram (instagram)

Connecting. A professional (Business or Creator) Instagram account. A personal account cannot publish through any API.

Needed to start the connection:

  • connection_type — Instagram login is the direct route and the default. Facebook login is for accounts reached through a Page. One of: instagram, facebook.

Caption. Up to 2200 characters.

Media. At least 1, at most 10; image or video. Stories take exactly one item. Reels take one video.

Placement. timeline, reels, stories.

Supports. schedule, carousel, thumbnail, collaborators, mediaTags, metrics.

TikTok (tiktok)

Connecting. A personal or creator TikTok account.

Caption. Up to 2200 characters.

Title. Optional, up to 90 characters. Used as the headline on photo posts.

Media. At least 1, at most 35; image or video. One video, or up to 35 images as a photo post. Videos and images cannot be mixed.

Supports. schedule, draft, carousel, metrics.

TikTok Business (tiktok_business)

Connecting. A TikTok Business account. Use it when you need business analytics; otherwise the ordinary TikTok connection is simpler.

Caption. Up to 2200 characters.

Title. Optional, up to 90 characters.

Media. At least 1, at most 35; image or video. One video, or up to 35 images as a photo post.

Supports. schedule, draft, carousel, thumbnail, metrics.

YouTube (youtube)

Connecting. A Google account with a YouTube channel. Uploads count against the channel's daily API quota.

Caption. Up to 5000 characters. Becomes the video description when no explicit description is given.

Title. Required, up to 100 characters. YouTube refuses an upload without a title.

Media. At least 1, at most 1; video. Exactly one video. A vertical video under three minutes is published as a Short by YouTube itself — there is no separate Shorts endpoint to call.

Required options. title.

Supports. schedule, thumbnail, metrics.

X (x)

Connecting. Posting through the X API depends on the access tier of the app the connection is made with.

Needed to start the connection:

  • connection_type — OAuth 2.0 is the default. OAuth 1.0a exists for apps still on the older credentials. One of: oauth2, oauth1.

Caption. Up to 280 characters. 280 is the limit for a standard account. A premium account may write far more, but Mellow cannot see the account's tier, so anything longer is reported rather than silently sent.

Media. Optional, at most 4; image or video. Up to four images, or one video. Text on its own is fine.

Supports. schedule, carousel, poll, metrics.

LinkedIn (linkedin)

Connecting. You need admin rights on the company page you want to publish to.

Needed to start the connection:

  • connection_type — A company page is the route available on the provider's shared credentials. A personal profile needs Mellow's own approved LinkedIn app. One of: organization, personal.

Caption. Up to 3000 characters, and required. LinkedIn publishes nothing without commentary.

Media. Optional, at most 20; image or video. Up to twenty images, or one video.

Supports. schedule, carousel, metrics.

Threads (threads)

Connecting. The Threads account attached to your Instagram login.

Caption. Up to 500 characters.

Media. Optional, at most 20; image or video. Text on its own is fine. Up to twenty items in a carousel.

Placement. timeline, reels.

Supports. schedule, carousel, metrics.

Bluesky (bluesky)

Connecting. Bluesky has no OAuth consent screen: the connection is made with a handle and an app password, so an agent cannot complete this step alone.

Needed to start the connection:

  • handle — Your full Bluesky handle, for example name.bsky.social.
  • app_password — Create one in Bluesky under Settings → App Passwords. Never your account password — an app password can be revoked on its own.

Caption. Up to 300 characters, and required. Bluesky counts graphemes, so emoji and combined characters can cost more than they look.

Media. Optional, at most 4; image or video. Up to four images, or one video.

Supports. schedule, carousel, metrics.

Pinterest (pinterest)

Connecting. A Pinterest business account with at least one board to pin to.

Caption. Up to 500 characters. Becomes the pin description.

Title. Optional, up to 100 characters.

Media. At least 1, at most 1; image or video. One image or one video per pin.

Supports. schedule, metrics.

Facebook (facebook)

Connecting. A Facebook Page you administer. Personal timelines cannot be published to through the API.

Caption. Up to 63206 characters.

Media. Optional, at most 10; image or video. Stories take exactly one item. Reels take one video.

Placement. timeline, reels, stories.

Supports. schedule, carousel, thumbnail, collaborators, mediaTags, metrics.


What a refusal means

Every error carries a code, a sentence, and retryable. Trust retryable: if it is false, the same request will fail the same way for ever, and the thing to change is the request.

A rejected post also carries issues, one per problem, each naming the channel and the field. Fix them together and try once, rather than one at a time.

Before anything is sent

CodeMeaningWhat to do
authentication_requiredNo credential, or one Mellow does not recognise.Present the key or token.
insufficient_scopeThe credential lacks a permission this call needs.The owner grants it when they create the key.
channel_not_connectedThat channel ID is not one of this owner's.Call list_channels; IDs are stable but connections come and go.
channel_out_of_scopeThe channel exists, but this key was not given it.Ask the owner, or publish to the channels you do have.
caption_too_longThe caption exceeds that network's limit.Give the channel a shorter caption with perChannel.
title_requiredYouTube will not accept an upload without a title.Set options.youtube.title.
option_requiredSomething the network insists on is missing.The message names it.
media_kinds_mixedImages and video in one post.Split the post, or give the channel its own media.
media_requiredThe network publishes nothing without media.Add media, or drop that channel.
schedule_in_pastscheduledAt has already passed.Omit it to publish now.
daily_limit_reachedThis key has used its day's posts.Wait, or the owner raises the limit.
idempotency_key_reusedThat key already published something else.Use a new key. This is a bug in the caller.

After it was sent

CodeMeaningWhat to do
provider_errorThe publishing provider refused or failed.Read retryable.
provider_result_failedThe network itself rejected this channel's post.The channel's errorMessage carries what the network said.
result_never_confirmedThe network never reported an outcome.Check the account directly before republishing — it may have gone out.
already_publishedCancelling something already public.Nothing here can unpublish it; delete it in the network.

Two failures worth naming

A partial post is not a failed post. status: "partial" means some channels published. Republishing the whole thing duplicates those. Republish only the channels whose target says failed.

A timeout is not a refusal. If create_post times out, the post may exist. Retry with the same idempotency key — that is exactly what it is for — or call list_posts and look.


Worked examples

Publish a vertical video everywhere it belongs

{
  "caption": "Three minutes on why we rebuilt the editor.",
  "media": ["https://cdn.example.com/editor.mp4"],
  "channels": ["spc_ig", "spc_tiktok", "spc_youtube", "spc_threads"],
  "options": {
    "instagram": { "placement": "reels", "shareToFeed": true },
    "youtube": { "title": "Why we rebuilt the editor", "privacyStatus": "public" },
    "tiktok": { "privacyStatus": "public", "allowComment": true }
  }
}

A vertical video under three minutes becomes a Short on YouTube by itself. There is no Shorts setting to send.

One thought, different lengths

Bluesky allows 300 characters and X allows 280; LinkedIn allows 3000 and rewards using them. Write the long version once and override the short ones:

{
  "caption": "The long version, written for LinkedIn…",
  "channels": ["spc_li", "spc_bsky", "spc_x"],
  "perChannel": {
    "spc_bsky": { "caption": "The short version." },
    "spc_x": { "caption": "The short version." }
  }
}

Schedule a week

Make one create_post per slot with scheduledAt, and give each a stable idempotency key — week-32-tue-morning rather than a random value — so re-running the plan does not double-post.

Prepare something for a person to approve

Under a review key, create_post prepares the post and stops. Tell the person it is waiting; they approve it in Mellow. whoami says which mode the key is in, so check before promising to publish.

Recover from a partial failure

get_post → status "partial"
         → targets[2].status "failed", errorMessage "…"

Fix what the message names, then create_post again with only the failed channel and a new idempotency key. Do not resend the channels that published.