Syndroo docs
Version 0.2.0-rc.1 unpublished release candidate Website GitHub

Bluesky setup

Bluesky is the first-platform path in these docs: one account, one app password, and text-only posting through the official @atproto/api SDK. This page covers the credentials, the Worker secrets they become, one single-platform request, and how to read the result.

Mock-tested, no live-account record Provider bluesky-native

Status and evidence

Syndroo 0.2.0-rc.1 is an unpublished release candidate: prepared in the repository, not published to npm, not tagged, and not deployed. Bluesky is recorded as mock-tested for this candidate.

Evidence: unit tests plus the local Mock SNS end-to-end gate in the core repository. That gate replaces only the network boundary with a loopback mock, so it proves wiring, persistence, idempotency, ambiguity handling and scheduling. It cannot prove provider permissions, current API compatibility or rate limits. No live-account acceptance record exists yet, and live acceptance is an open release gate. See Platform readiness and the capability record.

What you need

  • A Bluesky account you can post from. Use an account you are willing to publish real text with, because the commands on this page publish for real once the credentials are configured.
  • A Bluesky app password. App passwords are created separately from the account password and can be revoked on their own. Syndroo never asks for the account password.
  • A running Worker and its API key. Either a deployment in your own Cloudflare account or a local instance. The Worker holds the platform credentials; clients hold only SYNDROO_API_KEY. If you have not set one up yet, the first-post walkthrough covers the whole path with Bluesky.
  • Your handle, such as alice.bsky.social, and optionally the PDS hostname, which defaults to bsky.social.

The adapter reads three Worker secrets:

SecretRequiredValue
BLUESKY_IDENTIFIERYesYour Bluesky handle.
BLUESKY_PASSWORDYesAn app password, not the account password.
BLUESKY_HOSTNoPDS hostname. Defaults to bsky.social; set it only when your account lives on another PDS.

Create the app password by hand

App passwords are issued inside the account settings of the Bluesky client. The exact menu labels belong to Bluesky and can change, so treat the official documentation linked under Official sources as the authority.

  1. Sign in to Bluesky

    Use the account you intend to publish from. Nothing on this page needs the account password.

  2. Create an app password

    Open the app-password section of account settings and create one entry for this Worker, for example named syndroo-worker. A named entry is easier to revoke later without disturbing anything else.

  3. Copy the value once

    Copy the generated password as soon as it is shown and store it in the secret place your Worker reads. Treat it as a secret from that moment on; do not paste it into a page, a chat message or an issue.

An app password, not the account password.

An app password can be revoked on its own, and using one keeps your account password out of a deployment's secret store.

Add the secrets to the Worker

Put the values where your runtime reads secrets: Worker secrets for a deployment, .dev.vars for a local instance. Cloudflare deployment and Local development cover the exact commands for each path. The names are the same in both places.

BLUESKY_IDENTIFIER="your-handle.bsky.social"
BLUESKY_PASSWORD="your-bluesky-app-password"
BLUESKY_HOST="bsky.social"
Configuration is checked for shape, not for validity.

Before accepting a post, Syndroo checks that the required values exist and are non-empty. It does not call Bluesky to test them. A revoked or mistyped app password passes that check and surfaces later as a publication failure with errorCode AUTH.

Only the platforms you configure are selectable. Sending platforms: ["bluesky"] without these secrets returns HTTP 422 with PLATFORM_NOT_CONFIGURED before anything is stored or queued.

Publish and check one post

The commands below publish for real once the secrets above are configured. Set the two client variables in the terminal that will call Syndroo.

export SYNDROO_URL="https://your-worker.your-subdomain.workers.dev"
export SYNDROO_API_KEY="the-same-secret-your-worker-has"
curl -X POST "$SYNDROO_URL/v1/posts" \
  -H "Authorization: Bearer $SYNDROO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bluesky-setup-001" \
  --data '{
    "content": "First post from my own Syndroo Worker.",
    "platforms": ["bluesky"]
  }'

The accepted response is HTTP 202 with status queued:

{
  "id": "post_...",
  "status": "queued"
}

HTTP 202 means the request was accepted for processing. It does not confirm that Bluesky accepted anything, and it does not prove that the Queue has not already sent the platform request, because delivery can start immediately after acceptance. Read the stored state to learn the outcome:

curl \
  -H "Authorization: Bearer $SYNDROO_API_KEY" \
  "$SYNDROO_URL/v1/posts/post_..."
{
  "id": "post_...",
  "content": "First post from my own Syndroo Worker.",
  "platforms": ["bluesky"],
  "status": "published",
  "publications": [
    {
      "platform": "bluesky",
      "provider": "bluesky-native",
      "status": "published",
      "attempts": 1,
      "externalId": "at://did:plc:.../app.bsky.feed.post/...",
      "externalUrl": "https://bsky.app/profile/.../post/...",
      "errorAmbiguous": false
    }
  ]
}

queued and publishing are not terminal; poll the same URL. published and failed are terminal. The aggregate post status is published only when every selected platform succeeded, so with one platform selected it mirrors that publication.

Repeating the identical request with the same Idempotency-Key returns the stored result with HTTP 200 instead of creating a second post. See Idempotency-Key.

Limits, tokens and external dependencies

ItemRecorded for 0.2.0-rc.1
Text limitThe candidate enforces 300 Unicode code points and 3,000 UTF-8 bytes before the network call, counting the text by code point. The official lexicon allows 300 graphemes and 3,000 bytes, so this guard can be stricter than the provider for text built from combining marks or multi-code-point emoji; they are two different rules, not one shared limit. HTTP(S) URLs in the text receive link facets.
Content typeText only. Media, threads and rich embeds are outside this version.
Credential maintenanceYours. Revoke or replace the app password in Bluesky, then update the Worker secret. Syndroo has no OAuth flow and does not renew credentials.
External dependencyBluesky's own service, terms and limits apply. Syndroo controls none of them, includes no pricing of its own, and makes no promise about a provider's current plan, quota or limits.

Check the provider's current documentation before relying on a specific limit, scope or plan. Those facts change on the provider's schedule, not on this page; the links under Official sources are the references to re-check.

When something fails

A failed publication stores errorCode, errorMessage and errorAmbiguous in the post response. Read them before you act.

What you seeWhat it meansWhat to do
HTTP 422 PLATFORM_NOT_CONFIGUREDThe required Bluesky secrets are missing or empty.Add the secrets named above, then send the request again. Nothing was stored or queued.
AUTHBluesky rejected the credentials: revoked, mistyped, or the app password no longer exists.Create a new app password and update the Worker secret. Check the account before resending, because an earlier attempt may have succeeded before the credential broke.
INVALID_CONTENTThe text exceeded the adapter limit and nothing was sent.Shorten the text and publish again with a new key.
RATE_LIMITBluesky asked the caller to slow down.Syndroo retries this automatically with backoff, up to three attempts. Let it finish, then poll the same post.
PROVIDER_UNAVAILABLE, NETWORK or UNKNOWN, with errorAmbiguous: trueThe write may have reached Bluesky; the outcome is unknown.Check the account by hand. Syndroo never resends an ambiguous publication automatically.
Post status partialSome selected platforms succeeded and some failed.Never resubmit the platforms that succeeded. The failed ones need attention only after you have excluded an ambiguous outcome.
Rule out an ambiguous outcome before publishing again.

If a publication is failed with errorAmbiguous: true, check the platform account by hand first, because the post may already exist. Only when you have confirmed that nothing was published and you still want the post should you send a new request with a new Idempotency-Key. Reusing the old key replays the stored result instead of publishing. Read Ambiguous outcomes for the reasoning.

Official sources

This page follows the candidate's capability record and the product revision above. It was not verified against a live Bluesky account, so re-check the official documentation when a step, limit or permission differs from what you see.