Api Drafts And Scheduling
Drafts and scheduling
These routes change a real Bluesky account, so they all need a read and
write key. Scheduling additionally needs the scheduling feature on your
plan — check features in GET /me/.
One JSON shape covers both a single post and a thread:
{"posts": [{"content": "first"}, {"content": "second"}], "tags": ["launch"]}
One entry is a single post. Several make a thread, wired in order, with the first as the root.
Image upload is not in this version.
Drafts
| Route | Method | Scope |
|---|---|---|
/drafts/ |
GET | read |
/drafts/ |
POST | write |
/drafts/{id}/ |
GET | read |
/drafts/{id}/ |
PATCH, DELETE | write |
/drafts/{id}/schedule/ |
POST | write |
/drafts/{id}/publish/ |
POST | write |
POST /drafts/ returns 201 with {"root_id": 88, "ids": [88, 89], "posts": [...]}.
PATCH accepts content and tags. Tags are given as plain names and created
if they do not exist.
Another account's draft returns 404, not 403 — the same behaviour as the
rest of the app.
Publishing
POST /drafts/{id}/publish/ posts to Bluesky immediately and deletes the draft.
It is the only irreversible route here, so it requires:
{"confirm": true}
That is a deliberate speed bump. It makes accidental publication a two-step decision for an agent rather than a one-step one, and it gives you an obvious place to put a human in the loop.
If the account has no Bluesky credentials stored, you get 409
bluesky_not_connected rather than a server error — sign in on the web app
first.
Scheduled posts
| Route | Method | Scope |
|---|---|---|
/scheduled/ |
GET | read |
/scheduled/ |
POST | write |
/scheduled/{id}/ |
GET | read |
/scheduled/{id}/ |
PATCH, DELETE | write |
{"posts": [{"content": "..."}], "scheduled_at": "2026-09-01T14:30:00Z"}
GET /scheduled/ accepts ?status=pending|posted|failed.
Getting the time right
This is the easiest thing to get quietly wrong.
- With an offset or a trailing
Z, the time means exactly what it says. - Without one, it is read in the account's own timezone — the one
GET /me/reports. A naive14:00for a Lisbon account is 14:00 in Lisbon, not UTC. - The time must be in the future, or you get
409 scheduled_in_past.
Send an explicit offset whenever you can. It removes the ambiguity entirely.
Threads
Every post in a thread shares one scheduled time. Changing scheduled_at on any
member moves the whole thread, and deleting the root deletes all of it.