Bluesky
Mutation
Use the scheduleBlueskyPost mutation to schedule posts to Bluesky:
mutation ScheduleBlueskyPost($input: ScheduleBlueskyPostInput!) {
scheduleBlueskyPost(input: $input) {
success
errors {
field
message
}
post {
id
socialAccount {
id
username
}
publishingStatus
allowRepliesFrom
submissions {
id
text
order
postAt
contentWarning
languages
tags
gallery {
id
galleryMediaSet {
id
media {
id
url
mimeType
}
}
}
}
}
}
}
Input Parameters
ScheduleBlueskyPostInput
schedulingMode: QUEUE_NEXT when scheduling a new post, or the post is a draft.
See Drafts without a date.SPECIFIC_TIME, QUEUE_NEXT (default: SPECIFIC_TIME).
Only accepted when scheduling a new post. See Scheduling at the Next Open Slot.EVERYBODY, MENTIONED_USERS, FOLLOWED_USERS, NOBODYREADY_TO_PUBLISH, DRAFT (default: READY_TO_PUBLISH)BlueskyPostSubmissionInputType
Each post in the thread supports these parameters:
NONE, SUGGESTIVE, NUDITY, PORNid, name, or url. See Attaching Media.
Only supported when scheduling new posts, not when updating existing ones.hour, dayhour, dayExamples
Simple Post
Schedule a basic post with text only.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Hello Bluesky! This is my first scheduled post 🌤️",
"order": 0,
"contentWarning": "NONE"
}
]
}
}
Scheduling at the Next Open Slot
Instead of picking a time yourself, pass schedulingMode: QUEUE_NEXT and leave postAt out. Postpone puts the post in the next open slot on the account's Account Schedule, skipping any slot that already has a post on it.
{
"input": {
"username": "myhandle.bsky.social",
"schedulingMode": "QUEUE_NEXT",
"thread": [
{
"text": "This one goes out at my next open slot.",
"order": 0,
"contentWarning": "NONE"
}
]
}
}
The whole thread lands on that one slot, the same way an explicit postAt applies to every post in the thread.
The account needs a schedule set up for this to work, postAt and QUEUE_NEXT can't be combined, and updateScheduledBlueskyPost rejects schedulingMode. See Scheduling at the Next Open Slot for the full rules.
Post with Media URL
Schedule a post by uploading media from an external URL. The media will be automatically downloaded and added to your Content Library.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Check out this amazing sunset! 🌅",
"order": 0,
"contentWarning": "NONE",
"media": [{ "url": "https://example.com/sunset.jpg" }]
}
]
}
}
Post with Content Library Media
Schedule a post using media already in your Content Library. Reference the file by its id, which every upload mutation returns.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Sharing our company logo! 🏢",
"order": 0,
"contentWarning": "NONE",
"media": [{ "id": "1042" }]
}
]
}
}
You can also reference the file by name. Names are matched case-insensitively and the first match wins, so prefer id when several files share a name.
{
"media": [{ "name": "company-logo.png" }]
}
Thread with Multiple Posts
Schedule a multi-post thread with reply restrictions.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"allowRepliesFrom": "FOLLOWED_USERS",
"thread": [
{
"text": "🧵 Thread about building great APIs (1/3)",
"order": 0,
"contentWarning": "NONE"
},
{
"text": "First, always design your API with the developer experience in mind. Clear documentation and consistent patterns make all the difference.",
"order": 1,
"contentWarning": "NONE"
},
{
"text": "Second, implement proper error handling and rate limiting. Your API consumers will thank you when things go wrong (and they will).",
"order": 2,
"contentWarning": "NONE",
"final": true
}
]
}
}
Post with Content Warning
Schedule a post with appropriate content warning labeling.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Artistic photography from my latest shoot 📸",
"order": 0,
"contentWarning": "SUGGESTIVE",
"media": [{ "url": "https://example.com/photoshoot.jpg" }]
}
]
}
}
Multi-Language Post
Schedule a post with language tags for better discoverability.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Hello world! / Hola mundo! / Bonjour le monde!",
"order": 0,
"contentWarning": "NONE",
"languages": ["en", "es", "fr"],
"tags": ["multilingual", "greetings"]
}
]
}
}
Auto-Repost Configuration
Schedule a post with automatic reposting.
{
"input": {
"username": "myhandle.bsky.social",
"postAt": "2025-12-01T15:30:00Z",
"thread": [
{
"text": "Don't miss our latest blog post about API best practices! 📖",
"order": 0,
"contentWarning": "NONE",
"repostAtAmount": 2,
"repostAtUnit": "hour",
"repostRepeatDays": 3,
"repostFromSocialAccount": {
"username": "mycompany.bsky.social"
}
}
]
}
}
Response Types
Success Response
{
"data": {
"scheduleBlueskyPost": {
"success": true,
"errors": [],
"post": {
"id": "123",
"socialAccount": {
"id": "456",
"username": "myhandle.bsky.social"
},
"publishingStatus": "READY_TO_PUBLISH",
"allowRepliesFrom": "EVERYBODY",
"submissions": [
{
"id": "789",
"text": "Hello Bluesky! This is my first scheduled post 🌤️",
"order": 0,
"postAt": "2025-12-01T15:30:00Z",
"contentWarning": "NONE",
"languages": [],
"tags": [],
"gallery": null
}
]
}
}
}
}
Error Response
{
"data": {
"scheduleBlueskyPost": {
"success": false,
"errors": [
{
"field": "text",
"message": "Post text cannot exceed 300 characters."
}
],
"post": null
}
}
}
Updating a Post
To update an existing scheduled post, use the same input type with the corresponding update mutation and include the post id:
mutation UpdateScheduledBlueskyPost($input: ScheduleBlueskyPostInput!) {
updateScheduledBlueskyPost(input: $input) {
success
errors {
field
message
}
post {
id
}
}
}
Pass the id of the post you want to update in the input, along with the fields you want to change:
{
"input": {
"id": "12345",
"username": "myhandle.bsky.social",
"postAt": "2026-01-15T10:30:00Z",
"thread": [{"text": "Updated post text!", "order": 0, "contentWarning": "NONE"}]
}
}
id field to specify which post to update. Only scheduled posts that have not been published yet can be updated.postAt is required on an update, so every update states the date the post goes out on — pass the date it already has to leave it where it is. A draft staying a draft is exempt, and a draft you're promoting to READY_TO_PUBLISH needs a date of your choosing; see Drafts without a date.
schedulingMode is not accepted on an update at all; see Scheduling at the Next Open Slot.
Validation Rules
- Text Length: Maximum 300 characters per post
- Media: Supports images and videos with reasonable file size limits
- Content Warnings: Required field for all posts, use
NONEfor general audiences - Languages: Use standard IETF BCP 47 language codes (e.g.,
en,es,fr) - Threads: No hard limit on thread length
- Scheduling: Posts must be scheduled for future dates only
- Account Limits: Respects your plan's monthly post limits
Common Errors
The specified username is not connected to your Postpone account. Connect the account in your settings first.
The post text exceeds the 300 character limit. Consider breaking long posts into threads.
The postAt timestamp must be in the future. Check your timezone settings.
A new post has to say when it goes out: pass a postAt, or schedulingMode: QUEUE_NEXT. Drafts are exempt: leave postAt off one and it is parked on a placeholder date about 30 days out.
schedulingMode isn't accepted on an update, so a postAt is the only way to say when the post goes out. You'll also see it when promoting a parked draft to publishingStatus: READY_TO_PUBLISH without giving it a date.
schedulingMode: QUEUE_NEXT needs an Account Schedule on the account you're posting to. Set one up, or pass an explicit postAt.
schedulingMode is only accepted when scheduling a new post. To move a post that is already scheduled, pass a postAt to updateScheduledBlueskyPost.
The contentWarning field is required. Use NONE for general audiences or appropriate labels for adult content.
Language codes must follow IETF BCP 47 standard (e.g., en for English, es for Spanish).
You've reached your plan's post limit for the scheduled month. Upgrade your plan or schedule for a different month.