Skip to content
Postauto
Explore Postauto

MCP server and HTTP API

Your AI tools. Your creative workflow.

One server stands in front of every channel the account has connected, with 31 typed tools behind it. Your client reads the channels, reads what each network will actually accept, writes the post and sends it.

Self serve One key opens the MCP server and the HTTP API. You make it yourself, in Settings, and nothing goes out until you or your agent says so.

Connect

Pick the client you already have

Every one of these talks to the same endpoint and signs in the same way. Choose one for its own three steps, or take the terminal instead.

Any language, any runtime. One bearer key.

Speaks MCP over HTTP

01

Add it as a custom connector

Customize, Connectors, Add custom connector, and paste this as the remote MCP server URL, exactly as written and without a trailing slash. Leave authentication on: Claude reads two documents from this server, registers itself, and sends you to sign in and approve.

Connector URL
https://api.postauto.io/api/mcp

02

Or open Claude with it filled in

This link opens the same form with the name and the address already typed. What you approve is scoped, and the consent screen says so in plain words. publish_post and retry_target are marked destructive, so Claude asks before either reaches a timeline.

Open the form in Claude

03

Or paste a key, if the form cannot sign in

Some organisations' connector form has no sign-in for a custom server. Paste this as the server URL instead: it carries an API key in the path, authenticates exactly as a header does, and is a secret from then on. Make the key in Settings, API keys. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Connector URL
https://api.postauto.io/api/mcp/pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

MCP over HTTP, or from a terminal

01

Add the server

One command, no key. Claude Code registers itself with the server and works out where to sign in on its own.

Command
claude mcp add --transport http postauto https://api.postauto.io/api/mcp

02

Sign in

Run /mcp inside Claude Code and follow the browser. It stores the token and refreshes it when it expires, so this is the last time you think about it. From a shell instead, claude mcp login postauto does the same.

See what you are approving

03

Or use a key, for a script with no browser

A cron job cannot follow a browser, so it wants a key instead. Make one in Settings, API keys and pass it as a header. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Command
claude mcp add --transport http postauto \
  https://api.postauto.io/api/mcp \
  --header "Authorization: Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Speaks MCP over HTTP

01

Turn on developer mode, then add it

Settings, Security and login, Developer mode. Then chatgpt.com/plugins, the plus button, a name and a description, and this as the MCP server URL. Choose OAuth: ChatGPT registers itself with this server and sends you to sign in. A workspace admin does the same under Workspace settings, Apps, Create, and publishes it from Drafts.

Connector URL
https://api.postauto.io/api/mcp

02

Write actions ask first

ChatGPT confirms write actions by default; keep that on. Everything that reads runs without a question, and the two tools that reach a timeline are marked so a client can see it.

Every tool, and what each one can reach

03

Or a key, where you choose no authentication

Choose No Authentication and use this address instead: it carries an API key in the path and authenticates exactly as a header does. A published plugin cannot present a key, so this is for your own connector only. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Connector URL
https://api.postauto.io/api/mcp/pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Speaks MCP over HTTP

01

Write it into mcp.json

~/.cursor/mcp.json gives every project the server, .cursor/mcp.json in a repository gives one project it. No key: Cursor speaks OAuth, opens a browser, and holds its own token. This file is safe to commit.

Configuration
{
  "mcpServers": {
    "postauto": {
      "url": "https://api.postauto.io/api/mcp"
    }
  }
}

02

Or add it in one click

Cursor's own install link, carrying exactly the entry above. It opens Cursor, shows you the server, and asks before adding it.

Add to Cursor

03

Or a key in the file

The same entry with the key as a header, for a machine with no browser. A committed .cursor/mcp.json with a key in it is a published key, so ignore the file or keep this in the user scoped one. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
{
  "mcpServers": {
    "postauto": {
      "url": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

MCP over HTTP, or from a terminal

01

Add it to the CLI's settings

~/.gemini/settings.json for every project, .gemini/settings.json for one. httpUrl is the streamable HTTP form, which is what this endpoint speaks. With oauth enabled the CLI registers itself and /mcp auth postauto signs you in.

Configuration
{
  "mcpServers": {
    "postauto": {
      "httpUrl": "https://api.postauto.io/api/mcp",
      "oauth": {
        "enabled": true
      }
    }
  }
}

02

One line does the same, and the app takes it too

This command writes the entry above. The Gemini app takes the same address under Settings & help, Connected Apps, Custom apps for Spark, Add a custom app, for personal accounts in the US for now.

Command
gemini mcp add --transport http postauto https://api.postauto.io/api/mcp

03

Or a key in settings.json

The same entry with the key as a header, for a machine with no browser. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
{
  "mcpServers": {
    "postauto": {
      "httpUrl": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Speaks MCP over HTTP

01

Add a workspace server

.vscode/mcp.json, type http and the address, nothing to paste. VS Code handles the sign-in itself: a browser opens on the first connection, and the token is stored and refreshed for you.

Configuration
{
  "servers": {
    "postauto": {
      "type": "http",
      "url": "https://api.postauto.io/api/mcp"
    }
  }
}

02

Or install it in one click

VS Code's own install link, carrying exactly the entry above. The server then appears in the MCP view and its tools in the chat's tool picker.

Install in VS Code

03

Or a key, prompted for and kept by VS Code

The keyed form uses an input rather than the key itself: VS Code asks for it the first time and stores it, so this file is safe to commit too. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
{
  "inputs": [
    {
      "type": "promptString",
      "id": "postauto-key",
      "description": "Postauto API key (pau_live_...)",
      "password": true
    }
  ],
  "servers": {
    "postauto": {
      "type": "http",
      "url": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer ${input:postauto-key}"
      }
    }
  }
}

Speaks MCP over HTTP

01

Add it to settings.json

Under context_servers, with url. Settings, AI, MCP Servers does the same from a screen. A remote server is native in Zed: with no Authorization header set, it prompts you to sign in through the standard flow the first time it connects.

Configuration
{
  "context_servers": {
    "postauto": {
      "url": "https://api.postauto.io/api/mcp"
    }
  }
}

02

Nothing to bridge

An extension is only for a server that runs as a program on your machine. A remote one is added exactly as above, and that is the whole of it.

The server, in full

03

Or a key in the same entry

With an Authorization header set, Zed sends it and never asks you to sign in. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
{
  "context_servers": {
    "postauto": {
      "url": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Speaks MCP over HTTP

01

Write it into mcp_config.json

~/.codeium/windsurf/mcp_config.json, still the path the product reads under its new name. serverUrl is its word for the endpoint. Leave the headers out and Devin Desktop sends you to sign in.

Configuration
{
  "mcpServers": {
    "postauto": {
      "serverUrl": "https://api.postauto.io/api/mcp"
    }
  }
}

02

Or from the panel

MCPs in the Cascade panel, or Devin Settings, Cascade, MCP Servers, takes the same address and offers the same sign-in.

Connector URL
https://api.postauto.io/api/mcp

03

Or a key in the same entry

Add Authorization with Bearer and your key under headers on the same entry, for a machine with no browser. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
{
  "mcpServers": {
    "postauto": {
      "serverUrl": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

MCP over HTTP, or from a terminal

01

Add it, then sign in

The first command writes the entry into ~/.codex/config.toml; the second opens a browser. Codex registers itself with this server and holds its own token.

Command
codex mcp add postauto --url https://api.postauto.io/api/mcp
codex mcp login postauto

02

Or write config.toml yourself

The same entry by hand, in ~/.codex/config.toml or a project's .codex/config.toml. auth is oauth unless you say otherwise. The Codex desktop app takes the same address under Settings, MCP servers, Add server, Streamable HTTP.

Configuration
[mcp_servers.postauto]
url = "https://api.postauto.io/api/mcp"

03

Or a key, for a script with no browser

Export POSTAUTO_API_KEY first; Codex reads the key from that variable rather than writing it into the file. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Command
codex mcp add postauto --url https://api.postauto.io/api/mcp --bearer-token-env-var POSTAUTO_API_KEY

Speaks MCP over HTTP

01

Install it from Raycast

The Install MCP Server command, or Manage MCP Servers, Install New Server. Paste this as the URL, leave OAuth Type on Dynamic, press Sign In. Raycast registers itself with PKCE and stores the token encrypted. MCP is a Pro feature there.

Connector URL
https://api.postauto.io/api/mcp

02

Ending it is one action on either side

Logout Server in Raycast drops its token. Settings, Connected apps in Postauto ends the connection from this side, and the token stops working on the next request.

The server, in full

03

Or a key in the address

The form has an HTTP Headers field for a key; this address carries one in the path instead and needs no field at all. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Connector URL
https://api.postauto.io/api/mcp/pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

MCP over HTTP, or from a terminal

01

Add it as a custom connector in Grok

grok.com/connectors, New Connector, Custom, and paste this as the MCP server URL. Grok discovers the tools and completes the sign-in this server asks for. The same connector works on iOS and Android.

Connector URL
https://api.postauto.io/api/mcp

02

From Grok Build, one command

Writes the entry into ~/.grok/config.toml. A server that signs in opens a browser on first use, and the token lives under ~/.grok. The xAI API's remote MCP tools take the same address with a key as the authorization value.

Command
grok mcp add --transport http postauto https://api.postauto.io/api/mcp

03

Or a key, for a script with no browser

The same command with the key as a header. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Command
grok mcp add --transport http postauto https://api.postauto.io/api/mcp --header "Authorization: Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

MCP over HTTP, or from a terminal

01

Add it, name the auth, sign in

Three commands: the add writes the server, the configure says it signs in, the login opens a browser. openclaw mcp doctor postauto --probe afterwards says whether the tools are reachable.

Command
openclaw mcp add postauto --url https://api.postauto.io/api/mcp --transport streamable-http
openclaw mcp configure postauto --auth oauth
openclaw mcp login postauto

02

Or in the Control UI

Settings, MCP, Add server takes the same address with transport Streamable HTTP and auth OAuth. Either way it lands in ~/.openclaw/openclaw.json under mcp.servers.

Connector URL
https://api.postauto.io/api/mcp

03

Or a key, for a machine with no browser

The same add with the key as a header, and no configure or login afterwards. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Command
openclaw mcp add postauto --url https://api.postauto.io/api/mcp --transport streamable-http --header "Authorization: Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

MCP over HTTP, or from a terminal

01

Add it to config.yaml

~/.hermes/config.yaml, under mcp_servers. Hermes connects to a remote endpoint directly and runs the sign-in on first use; the token is cached under ~/.hermes/mcp-tokens.

Configuration
mcp_servers:
  postauto:
    url: "https://api.postauto.io/api/mcp"
    auth: oauth

02

Leave the confirmation on

Two tools reach real timelines and nothing can take a post back: publish_post and retry_target. Both are marked so a client can see it and ask you first. Everything else reads, or writes a draft that waits for you.

Every tool, and what each one can reach

03

Or a key in the same entry

The same entry with the key as a header, for a machine with no browser. A key can publish to every channel on the account and it does not expire, so give an agent its own and revoke it in Settings, API keys when you are done.

Configuration
mcp_servers:
  postauto:
    url: "https://api.postauto.io/api/mcp"
    headers:
      Authorization: "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

The postauto CLI, or plain HTTP

01

Install the CLI and sign in

One package, no dependencies. auth login opens the browser, you approve, and the token stays in a file only you can read. postauto mcp is also the bridge for a client that only launches programs: it speaks stdio to the client and HTTP to us.

Command
npm i -g postauto
postauto auth login

02

Then write a post

Every command is one tool call, so what the CLI can do is exactly what an agent can do: accounts, networks, posts, draft, publish, inbox, and call for any of the rest. Answers are JSON.

Command
postauto draft --to <channelId> --text "Doors open at seven." --next-gate

03

Or plain HTTP, on a key

This is the shape of every MCP call, and both headers are required by the transport. It answers with the whole tool list. The REST API is the same service layer behind a different door, on the same key.

Command
curl -sS -X POST https://api.postauto.io/api/mcp \
  -H "Authorization: Bearer $POSTAUTO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Nothing on that list is the one you use? Anything that speaks MCP over HTTP works, and anything that does not can still call the plain HTTP API on the same key.

The short version

What is MCP, and what is actually happening?

A protocol for handing a model a set of typed tools

A client you already use, a chat app or an editor or a terminal agent, asks a server what it can do. The server answers with a list of tools, each one with a name, a description and a typed set of arguments. From then on the model can call them, and the client shows you what it called and what came back.

Postauto is one of those servers. It registers 31 tools in front of 16 networks, so the model never learns 16 network APIs, never holds 16 sets of tokens, and never guesses at 16 sets of media rules.

One POST, one answer, nothing to keep open

Transport is streamable HTTP in stateless mode. There is no session to open and none to close, which is why the whole setup is a URL and a header rather than a process to supervise. GET and DELETE answer 405, because there is nothing for them to do.

Authentication is a bearer token on every call. The same token authenticates the HTTP API, so a caller with no MCP in it is not a second integration, it is the same one through a different door.

MCP server and HTTP API

Publish to 16 networks from your own agent.

One MCP server and one HTTP API stand in front of every channel the account has connected. Your agent reads the channels, reads what each network will actually accept, writes the post and sends it. It never learns 16 network APIs, holds 16 sets of tokens, or handles 16 sets of media rules.

Terminal Claude Code
claude mcp add --transport http postauto \
  https://api.postauto.io/api/mcp \
  --header "Authorization: Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

One command, and the tools below are in the session. The same key opens the HTTP API.

What that looks like in a session

halyard-site claude
  1. Typed post the Guji restock to X, Bluesky and Threads
  2. Called list_accounts ()
  3. Returned 3 channels, all active: x, bluesky, threads
  4. Called get_network_rules (network: "x")
  5. Returned maxLength 280, counted weighted, threads true
  6. Called create_post (content, targets, idempotencyKey)
  7. Returned draft b21e6c78, 3 targets pending
  8. Answered Drafted for all three. X gets the short version, so it fits in one post. Send it?
  9. Typed send it
  10. Called publish_post (postId, idempotencyKey)
  11. Returned published, 3 of 3 departed

The post, where it landed

  1. X @halyardcoffee Departed x.com/halyardcoffee/status/1897402244
  2. Bluesky @halyardcoffee.bsky.social Departed bsky.app/profile/halyardcoffee.bsky.social/post/3l4c
  3. Threads @halyardcoffee Departed threads.net/@halyardcoffee/post/C9xNqTr

Step zero

One key opens both doors

Keys are self serve. There is no waitlist, no form and nobody to email. The same key authenticates the MCP server and the HTTP API, and it is the only thing either of them needs.

  1. One

    Open Settings, API keys

    Sign in and go to Settings, API keys. An account may hold up to 3 active keys, so an agent can have its own and you can revoke it without breaking anything else.

  2. Two

    Copy it once

    A key is pau_live_ followed by 32 characters, and it is shown exactly once. Postauto stores only a SHA-256 hash of it, so it cannot be shown again. Lose one and you revoke it and make another.

  3. Three

    Send it as a bearer token

    Authorization: Bearer pau_live_... on every call. Treat it like a password: it can publish to every channel on the account. It cannot mint another key, and it cannot reach anybody else's account.

Two ways to send it

The header is the one to use. It keeps the key out of URLs, which means out of proxy logs, browser history and shell history.

Some MCP clients still cannot attach a custom header. For those, and only for those, the key goes in the path instead. It authenticates identically. If that address ends up somewhere shared, revoke the key.

Path style fallback
https://api.postauto.io/api/mcp/pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The address

One endpoint, and the file it goes in

The endpoint is https://api.postauto.io/api/mcp. Transport is streamable HTTP in stateless mode: one POST, one answer, no session to open and none to close. Anything that speaks MCP over HTTP works, and GET and DELETE answer 405 because there is nothing for them to do.

.mcp.json project scoped
{
  "mcpServers": {
    "postauto": {
      "type": "http",
      "url": "https://api.postauto.io/api/mcp",
      "headers": {
        "Authorization": "Bearer pau_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Committing it to the repository

A file like this next to your code gives everyone on the project the same tools in their session, which is usually what you want.

A file with a key in it does not belong in version control. Add it to .gitignore, or keep the server in your own user scoped configuration and leave the repository clean.

Checking a key by hand

This is also the shape of every call the server takes. Both headers are required by the transport: without the Accept line a compliant client would not be compliant, and the server answers 406.

It answers with the 31 tools below. A missing or revoked key answers 401 with a JSON-RPC error explaining how to send one.

Terminal tools/list
curl -sS -X POST https://api.postauto.io/api/mcp \
  -H "Authorization: Bearer $POSTAUTO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

The surface

31 tools, and no catch-all

Every tool is explicit and typed. There is no wrapper that will call any endpoint you name, on purpose: it hands a model a surface nobody has read, and the client directories reject it anyway. The natural order for an agent is account_status for the ceilings, then list_accounts, then get_network_rules for each target, then create_post.

Read the account

What this key can reach, filed the way the customer files it, and how much of the plan is left. An agent that reads its ceilings first does not discover them on the tenth post.

list_accounts Reads
Every connected channel with its id, network, handle, posting times and always-on sign-off. Those ids are what create_post takes as targets.
list_clients Reads
The clients an agency files its channels under, and how many channels sit under each.
resolve_client Reads
Turn a name a person said into channel ids, so "post this to Acme" is a call rather than a guess. An ambiguous name is refused, not chosen between.
account_status Reads
The plan, its channel and monthly post limits, what has been used, and what is left.
list_tags Reads
The tag vocabulary this account files posts and media under.
list_media Reads
Search the media library, so a picture can be reused instead of fetched again.

Write a post

Read the network's real rules, cut the text to fit them, then create the post. With no departure time it is saved as a draft for a person to approve, which is usually the right default for an agent.

get_network_rules Reads
Per-network rules: the length ceiling and the unit it is counted in, the shapes a post can take, whether an @mention reaches anybody, and the real per-format file rules.
split_into_thread Reads
Cut one long piece of writing into thread parts that fit a network, measured the way that network measures.
next_slot Reads
The next open gate across a set of channels.
upload_from_url Writes
Fetch an image or video from a public address and return a media id. Private, loopback and link-local addresses are refused, and every redirect hop is checked again.
create_post Publishes
Create a post for one or more channels, with at most one of scheduledAt, nextSlot or publishNow. Marked as agent-created, so the account can always see which posts an agent wrote.
update_post Writes
Revise a post that has not gone out yet, keeping its id, its share link and its tags. Refused once anything has departed.

Send it, and repair it

The half that reaches an audience. Both of the first two take an idempotencyKey, and this is the pair worth using it on: a retry after a timeout is what a well written client does with a network error, and without a key it is also how a post publishes twice.

publish_post Publishes
Send a post that is already written, now, without waiting for its schedule. Takes a postId, skips the channels that already published, and takes an idempotencyKey so a retry after a timeout cannot deliver twice.
retry_target Publishes
Fly one channel of a post again after it failed, leaving every other channel untouched. Its targetId comes from get_post, and is not the account id.
delete_post Writes
Delete a post and its channels. Refused while the post is departing.

Find out what happened

The questions an agent asks afterwards: did it land, which channel refused it and in whose words, what performed, and what needs a person.

list_posts Reads
Recent posts with per-channel statuses and error text, newest first, paged.
search_posts Reads
Find a post by its words, status group, network or date range, so "the post about the launch" has an answer that is not a year of paging.
get_post Reads
One post by id, with its channels, their ids, their statuses and their published addresses.
get_post_metrics Reads
The latest numbers for each channel one post went to, and when they were read.
top_posts Reads
The best performing posts over a window, ranked by engagement rather than by views.
list_notifications Reads
What needs attention and is not attached to a post you already know: expired channels, stopped feeds, suspended webhooks, frozen metrics, billing.
acknowledge_notifications Writes
Mark notifications read, for the agent and for the person whose account this is.

Post without being asked

Two standing instructions: turn a feed into posts, and send a post that did well a second time. Both create posts on their own schedule, which is why both are set up deliberately and neither can be triggered on demand.

list_feeds Reads
The RSS or Atom feeds this account watches, with what each has produced.
preview_feed Reads
Read a feed without saving it: its title and three items rendered exactly as they would be posted. Call this first, because a blog address that is not a feed address saves quietly and then never posts.
watch_feed Writes
Turn new items in a feed into posts on the channels you choose. Everything already in the feed is recorded as seen, so a blog with years of archive contributes nothing until it publishes again.
update_feed Writes
Change a feed's channels, its post text, its mode, or whether it is watched at all.
unwatch_feed Writes
Stop watching a feed.
list_repost_rules Reads
The rules that send a past post again, with what each has sent.
create_repost_rule Writes
Send a post that performed well a second time, inside limits you set: cooldownDays, maxReposts and lookbackDays. A rule never reposts its own output.
update_repost_rule Writes
Change a rule's limits, its channels, or whether it runs.
delete_repost_rule Writes
Remove a rule. The posts it already sent are left alone.

The call the session made

create_post takes content and targets, plus optional media, overrides, parts, tags and per-network settings, and at most one of scheduledAt, nextSlot or publishNow. With none of those three the post is saved as a draft for a person to approve, which is usually the right default for an agent running on its own.

idempotencyKey is what makes a retry after a timeout safe. Send the same key again and you get the first call's answer back rather than a second post. Keys are honoured for 24 hours.

create_post arguments
{
  "content":
    "Six bags of the Ethiopia Guji left, then it is gone until March.",
  "targets": [
    "0a9c2f1e-6b7c-4a1e-9d3f-2b5c8e7a1d40",
    "7d1b4c30-11aa-4f52-8a90-3c6d5e2f1b88",
    "c4f8a5b2-90d7-4e63-b1a8-5f2c6d3e9a11"
  ],
  "idempotencyKey": "guji-restock-2026-09-01"
}

Before you wire it into a loop

What it will not do

The refusals are the interesting part of an API an agent drives unattended. Each of these is something the server actually refuses, and the message it returns says which channel, which limit or which id was the problem.

There is no tool that calls any endpoint

Every tool is explicit and typed, on purpose. A catch-all wrapper is rejected by both major client directories, and it hands a model a surface nobody has read.

Publishing cannot be undone

Not by this API, not by the app, and on several networks not by the network. publish_post and retry_target are annotated so a client can see it and ask a person first. On a post nobody has read, ask.

A post that has left cannot be edited

draft, scheduled and failed can be revised. publishing, published and partial are refused with the reason, and the check happens inside the write, so a scheduled post that departs while your call is in flight is refused rather than edited on its way out.

Let the server fit the text to each network

Every network counts differently. X weighs a whole emoji family as 2, Bluesky counts it as 1, and Threads counts the string in UTF-8 bytes, where one curly apostrophe is worth 3. get_network_rules returns the unit, and split_into_thread does the arithmetic.

Some failures are refused rather than retried

If a publish was interrupted after it started and before the network answered, nobody knows whether it went out. That channel is refused, by name, and takes confirmMayAlreadyBeLive on the next call once you have opened the channel and looked.

Nothing runs a feed or a rule on demand

There is deliberately no tool for it. Both create posts, and an agent with a loop is the caller most likely to call one in one. The schedule reaches the same result within the half hour, and create_post is there when a post is wanted now.

The other front door

Plain HTTP, for a caller with no MCP in it

The REST API is the same service layer under a different door, on the same key, at https://api.postauto.io/api/v1. It lists channels and networks, splits a thread, finds the next gate, creates, reads, revises and deletes posts, reads their numbers, and manages feeds and repost rules.

Terminal POST /api/v1/posts
curl -X POST https://api.postauto.io/api/v1/posts \
  -H "Authorization: Bearer $POSTAUTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Six bags of the Ethiopia Guji left.",
    "targets": ["0a9c2f1e-6b7c-4a1e-9d3f-2b5c8e7a1d40"],
    "nextSlot": true
  }'

Where the two doors differ

Two things are MCP only today, and both are the finishing half of publishing: publish_post, which sends a post that is already written, and retry_target, which flies one channel again after it failed. Over HTTP a post goes out by being scheduled, or by passing publishNow when you create it.

Reading a customer's own client names, searching posts by their words and reading notifications are MCP only as well. Everything else is on both.

How much you may ask for

120 a minute

Reads, per key

Every GET on /api/v1.

30 a minute

Writes, per key

POST, PATCH, PUT and DELETE on /api/v1, and every MCP call including the tool list, because every MCP call is a POST.

20 a minute

Per address, before the key

Charged on every request that reaches the API, so a bad key cannot be guessed at in a loop.

When something answers with a number

401
No header and no path key, or the key was revoked. Make a new one in Settings, API keys.
405
The client sent GET or DELETE. The endpoint is stateless and answers POST only, so there is no stream to open and no session to end.
406
The client did not send Accept: application/json, text/event-stream. Every compliant MCP client does.
429
A budget above is spent. Wait the seconds named in Retry-After.

Anything else that comes back as a message rather than as data is the answer: it names the network that refused the post, the limit that was passed, or the id that does not exist.

Make a key

Make a key and try it

Connect one channel, add the server to your client, and ask it to draft something. The draft waits for you, and nothing goes out until you say so.