Bluesky
Publish posts, threads, replies, images, videos and link cards to Bluesky (AT Protocol), and delete them, through the Postgale API and MCP server.
Postgale publishes to Bluesky accounts: text posts with links, mentions and hashtags, up to 4 images or one video, link cards, threads
and replies, and deletes of what was published. Everything is available over the REST API (paths relative to https://postgale.com/api/v1)
and the MCP server (connect_account, create_post, manage_published_post). Each connected Bluesky account has platform
bluesky; its platformAccountId is the account's DID (did:plc:…).
Connect an account
Bluesky accounts connect with their handle and an app password. Postgale never needs the account password.
- In Bluesky, open Settings → Privacy and security → App passwords and choose Add App Password. Name it (e.g. Postgale),
leave direct-message access off, and copy the password: it looks like
xxxx-xxxx-xxxx-xxxx. - In Postgale open Dashboard → Accounts → Bluesky, enter the handle (e.g.
yourname.bsky.socialor your own domain) and paste the app password.
Or connect from your code (the answer is the connected account; account.connected is sent as well):
curl -X POST https://postgale.com/api/v1/connect/bluesky \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{"handle":"yourname.bsky.social","appPassword":"xxxx-xxxx-xxxx-xxxx","externalId":"customer-42"}'
# {"platform":"bluesky","account":{"id":"…","platform":"bluesky","accountName":"@yourname.bsky.social","platformAccountId":"did:plc:…","externalId":"customer-42"}}Postgale resolves the handle to its DID and personal data server (PDS), works on any PDS (bsky.social or self-hosted), and signs in there. The session tokens and the app password are stored encrypted; sessions are renewed automatically, and if the renewal fails Postgale signs in again with the app password. A wrong handle or password answers 400 with the reason; anything other than an app password is refused.
If you delete the app password in Bluesky (or change the account password), the next call fails: the account becomes
needs_reconnect and account.disconnected is sent (reason token_invalid). Connect it again with a new app password; the same
account is updated, its post history stays.
Publish
The post content is the text, up to 300 characters as Bluesky counts them (graphemes: an emoji with modifiers counts once).
Links, @handle mentions and #hashtags in the text become clickable: mentions are resolved to the person's DID and left as plain text
when the handle does not exist. Without media, the first link gets a link card (title, description and image from the page) when
the page can be read; a card that cannot be built never fails the post.
curl -X POST https://postgale.com/api/v1/posts \
-H 'Authorization: Bearer sk_…' \
-H 'Content-Type: application/json' \
-d '{"content":"Our spring release is out 🌱 https://example.com/blog/spring #release","platforms":[{"accountId":"acc_bsky","platformSpecificData":{"mediaUrls":["https://media.postgale.com/…/hero.jpg"],"altTexts":["The new dashboard in dark mode"],"langs":["en"]}}],"publishNow":true}'| platformSpecificData | What it does |
|---|---|
mediaUrl / mediaUrls | Up to 4 images (JPEG, PNG, WebP or GIF, each ≤1,000,000 bytes) or one MP4 video (≤100 MB, ≤3 minutes). Images and a video cannot be mixed. Public https URLs, e.g. an uploaded media publicUrl |
altText / altTexts | Alt text for the image(s) or the video (≤2000 characters each) |
threadItems | A thread: up to 25 posts of {content, mediaUrl?, mediaUrls?, altText?, altTexts?}, each a reply to the previous one. Replaces the post text |
replyTo | Reply to an existing post: its at:// URI or a https://bsky.app/profile/<handle>/post/<id> link |
langs | Up to 3 language tags, e.g. ["en"] |
disableLinkPreview | No link card |
Limits are checked when the post is created; file sizes, types and the video length are checked again when the files are fetched,
before anything is posted. The target's platformPostId is the post's at:// URI and url its https://bsky.app/profile/<handle>/post/<id>
link. For a thread they are those of the first post; if a later post of the thread fails, the thread stops there and the target gets a
warning (the published posts are not repeated).
Delete
| Method | Path | What it does |
|---|---|---|
| POST | /posts/{id}/unpublish | Delete the post from Bluesky (for a thread: the first post) |
| GET | /accounts/{id}/health | Whether the session still works |
Deletes are queued for a person while approval applies to the API key, the organization or the account (202 pending_approval; MCP
manage_published_post returns the queued action). Bluesky posts cannot be edited.
Bluesky's rules to know
- Rate limits: when Bluesky answers 429, Postgale retries after the reset time Bluesky gives.
- Revoked app passwords: an app password Bluesky refuses marks the account
needs_reconnect(reconnect_requiredon the API) and the post is not retried automatically. - Analytics: likes, reposts and views are not collected yet, so post and account analytics answer
not_supported.
Discord: coming soon
Publish messages with attachments and embeds to Discord channels through a channel webhook, and edit or delete them, via the Postgale API and MCP server.
Mastodon
Publish statuses, threads and replies with media, alt text, content warnings and visibility to any Mastodon server through the Postgale API and MCP server.