> ## Documentation Index
> Fetch the complete documentation index at: https://docs.publishbuddy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schedule and queue posts

> Three ways to control when a post goes live — publish now, schedule for a fixed datetime, or queue into a recurring timeslot.

PublishBuddy supports three `publish_type` values. Picking the right one depends on the use case.

| Use case                                | `publish_type`                  |
| --------------------------------------- | ------------------------------- |
| Push live immediately                   | `now`                           |
| Publish at a specific UTC datetime      | `schedule_using_fixed_datetime` |
| Slot into the profile's recurring queue | `queue_using_timeslots`         |

## Publish now

Useful for breaking news, manual triggers, or scripts that run on cron and publish immediately.

```bash theme={null}
curl -X POST "https://api.publishbuddy.com/v1/workspaces/$WORKSPACE_ID/posts" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "'"$PROFILE_ID"'",
    "text": "Breaking: our Series B is closed.",
    "publish_type": "now"
  }'
```

The handoff to the social network happens asynchronously — `status` will be `publishing` for a few seconds, then `published`.

## Schedule for a fixed datetime

When you know exactly when a post should go live. Always send `publish_at` in UTC.

```bash theme={null}
curl -X POST "https://api.publishbuddy.com/v1/workspaces/$WORKSPACE_ID/posts" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "'"$PROFILE_ID"'",
    "text": "Merry Christmas from the team!",
    "publish_type": "schedule_using_fixed_datetime",
    "publish_at": "2026-12-25T09:00:00Z"
  }'
```

The post is created with `status: waiting` and `publish_at` set. PublishBuddy's workers pick it up at the scheduled minute, transition it to `publishing`, and push to the network.

<Tip>
  Need to publish at a user's local time? Convert to UTC client-side using the user's IANA timezone (`Australia/Sydney`, `America/New_York`, etc.). The API stores and reasons exclusively in UTC.
</Tip>

### Editing or cancelling a scheduled post

While the post is still `waiting`, you can update it (change text, swap media, push the time back) with `PUT /posts/{post}`, or cancel it entirely with `DELETE /posts/{post}`. Once the worker picks it up (status → `publishing`), it's too late.

## Queue using timeslots

The most powerful publish type. Every profile has a configurable **weekly queue** — a set of recurring timeslots like "Tuesday 9am, Thursday 2pm, Saturday 11am". Posts created with this publish type are slotted into the **next available** timeslot for the target profile.

```bash theme={null}
curl -X POST "https://api.publishbuddy.com/v1/workspaces/$WORKSPACE_ID/posts" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "'"$PROFILE_ID"'",
    "text": "Behind the scenes of this week's shoot.",
    "media_content_ids": ["'"$MEDIA_ID"'"],
    "publish_type": "queue_using_timeslots"
  }'
```

The created post has `status: waiting`, a `queue_id`, and a `publish_at` set to the timeslot it was assigned to. If your script bulk-creates 30 queued posts, they'll spread across the next 30 timeslots in the profile's queue.

<Warning>
  **Timeslots are profile-local.** Each profile has its own queue. There's no API endpoint to edit timeslots — they're configured in the dashboard under **Profile → Queue**.
</Warning>

### Reordering inside the queue

You can change the publish order by updating each post's `publish_at` (which moves it to a different existing timeslot) via `PUT /posts/{post}`. The queue editor in the dashboard offers a drag-and-drop UI for the same operation if you want a UI-driven workflow.

## Bulk scheduling

A common use case: import a CSV of 50 planned posts and schedule them across the next month. The recipe:

1. Upload each row's media via the presign → upload → confirm flow (batch up to 10 files per presign/confirm call — see [Upload and manage media](/guides/upload-media)).
2. For each row, `POST /workspaces/{workspace}/posts` with the right `publish_type`.
3. Track the returned `post.id` so you can update or cancel later.

Watch out for the rate limit (`throttle:api_1000_per_min` on the posts endpoint group — see [Rate limits](/api-essentials/rate-limits)). For most bulk imports, a `Promise.allSettled` with a concurrency limit of \~20 is comfortable.

## Status of all scheduled posts

```bash theme={null}
curl "https://api.publishbuddy.com/v1/workspaces/$WORKSPACE_ID/posts?status=waiting" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

Use the `status` filter to slice by lifecycle stage (`draft`, `needs_approval`, `waiting`, `published`, `failed`). Combine with the `from` / `to` query parameters to scope to a date range.

## Common scheduling gotchas

<AccordionGroup>
  <Accordion title="Post stays in 'publishing' forever" icon="hourglass-half">
    Almost always caused by an expired profile connection. Check the profile in the dashboard — if it shows "needs reconnection", the user has to complete the OAuth flow again. After reconnecting, retry the post.
  </Accordion>

  <Accordion title="publish_at in the past" icon="clock-rotate-left">
    Sending a `publish_at` value in the past returns `422`. If you want to publish immediately, use `publish_type: "now"` instead.
  </Accordion>

  <Accordion title="Queue is empty" icon="list">
    `publish_type: queue_using_timeslots` requires the profile to have at least one timeslot defined. If the queue is empty, the create call returns `422`. Add timeslots in the dashboard or switch to a fixed datetime.
  </Accordion>

  <Accordion title="Daylight savings transitions" icon="globe">
    Because PublishBuddy stores everything in UTC, DST transitions don't move scheduled posts. A post scheduled for "09:00 UTC" stays at 09:00 UTC even when local time shifts by an hour. If your end users expect "9am local always", convert from local → UTC at scheduling time and accept that the UTC value will shift by an hour twice a year.
  </Accordion>
</AccordionGroup>
