Recipe: publish a product update
One release note, two destinations, and wording that fits each platform. This is the smallest realistic cross-posting job: a shared body, one override, one idempotency key, and a result you read back instead of assuming.
Inputs
- A release identifier you can reproduce, such as a tag or a version string. It becomes the idempotency key.
- Shared text for every platform, and optionally shorter text for one of them.
- A Worker URL and key in the environment:
SYNDROO_URLandSYNDROO_API_KEY. - Configured platforms. This recipe uses
blueskyandthreads; both need their credentials before the request is accepted (Bluesky, Threads).
Steps
-
Write both versions inside their limits
The candidate enforces 300 Unicode code points and 3,000 UTF-8 bytes for Bluesky, and 500 characters for Threads. The API caps the shared body at 10,000 code points, and a per-platform limit that is exceeded later fails that publication with
INVALID_CONTENTinstead of being truncated. -
Send the request
The key is derived from the release identifier, so repeating this exact request after a network failure returns the original post instead of publishing twice.
curl -X POST "$SYNDROO_URL/v1/posts" \ -H "Authorization: Bearer $SYNDROO_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: release-0.2.0-rc.1" \ --data '{ "content": "Syndroo 0.2.0-rc.1 candidate: one HTTP call accepts a post and queues one publication per platform.", "platforms": ["threads", "bluesky"], "overrides": { "bluesky": { "content": "0.2.0-rc.1 candidate: Mock SNS gate green for Bluesky and Threads. Live accounts still pending." } } }' -
Expected acceptance
{ "id": "post_...", "status": "queued" }Keep the
id. HTTP202does not confirm platform success, and it may already be out of date when it reaches you: the Queue consumer can have published before you read the response. Poll the post for the real outcome. -
Read the per-platform result
Query the post with the id from the receipt:
curl \ -H "Authorization: Bearer $SYNDROO_API_KEY" \ "$SYNDROO_URL/v1/posts/post_..."The response carries one publication entry per platform:
{ "id": "post_...", "status": "published", "publications": [ { "platform": "threads", "status": "published", "attempts": 1, "externalId": "1789..." }, { "platform": "bluesky", "status": "published", "attempts": 1, "externalId": "at://...", "externalUrl": "https://bsky.app/..." } ] }Show the platform's own link when it returned one. A platform that returned only an identifier gets only that identifier shown; do not construct a URL from it.
Failure handling
| What you see | What happened | What to do |
|---|---|---|
Post status partial |
One platform published and the other did not. | Report per platform. There is no retry endpoint: first confirm errorAmbiguous is false on the failing entry, fix the cause, and then decide whether to send a new request with a new key that lists only the platforms you confirmed failed. Never include the platforms that already published. |
errorCode: "RATE_LIMIT", attempts: 3 |
Retries were attempted and the retry budget is exhausted. | Wait for the platform's window to pass, then publish a new request. Do not resend the whole original request against every platform. |
errorCode: "INVALID_CONTENT" |
The text exceeded the platform's own limit after the API accepted it. | Shorten that platform's override and send a new request with a new key. |
errorAmbiguous: true |
The platform may have accepted the post. | Check the account by hand before doing anything else. Syndroo never resends an ambiguous publication. |
HTTP 409 IDEMPOTENCY_CONFLICT |
The release key was already used with different text. | Decide whether this is the same post or a new one. Same post: resend the original body. New post: use a new key. |
Applies to
Syndroo 0.2.0-rc.1 (unpublished candidate). Statuses, fields and limits are those documented in the HTTP API reference; per-platform wording needs the platform's own guide.