---
title: "Spread many drafts across your preset posting times"
method: POST
path: "/schedule"
tags: ["Scheduling"]
---

# Spread many drafts across your preset posting times

`POST /schedule`

## Request

- Base URL: `https://www.juzpost.com/api/cli/v1`
- URL: `https://www.juzpost.com/api/cli/v1/schedule`
- Auth: HTTP bearer

The bulk scheduler. Resolves a group (or an explicit account list) to targets, then
assigns drafts to your preset wall-clock times, `minPerDay` posts per day,
walking forward from `startDate` and skipping any slot less than five minutes away.
Every post goes to every resolved account. Set `dryRun` to true to see the plan without
creating any timers. Always do this first. At most 50 posts are scheduled per call;
the rest come back in `skipped` with a reason, so call again to continue. Targets are
capped at 10 accounts.

## Request body

- SmartScheduleRequest: Inputs for the bulk scheduler. You must supply either `group` or a non-empty `socialAccountIds`; if both are given, `socialAccountIds` wins.
  - `minPerDay` integer, required: How many posts to place per day. Must not exceed the number of available time slots, or the call fails with `422`. Add more times or lower this number.
  - `group` string: Name of the channel group whose accounts should receive every post, matched exactly as returned by `GET /groups`. Unknown names return `404`.
  - `socialAccountIds` string[]: Explicit accounts to receive every post, used instead of `group`. Duplicates are collapsed. More than 10 distinct accounts is rejected with `422`.
  - `postIds` string[]: The specific drafts to schedule, in the order you want them placed. Omit this to use every draft you have, oldest first, up to 200. Any id that is not one of your drafts is reported in `skipped` rather than failing the call.
  - `startDate` string, date: First calendar day to place posts on, as `YYYY-MM-DD` in your timezone. Defaults to tomorrow. Slots less than five minutes away are skipped, so a start date in the past simply yields fewer usable slots.
  - `times` string[]: Wall-clock posting times as `HH:MM` in your timezone, overriding the your defaults for this call only. Times are sorted ascending and the earliest `minPerDay` of them are used each day.
  - `dryRun` boolean: When true, compute and return the plan without creating any timers. Run this first to confirm the dates look right, then repeat without it.

## Response `200`

The plan, plus what was scheduled and what was skipped. Individual entries in
`scheduled` may carry `status: failed` with an `error`, so check them.

- SmartScheduleResponse: The computed plan plus its outcome.
  - `scheduled` ScheduledEntry[], required: Every post that was placed, with its fire time. On a dry run these are proposals and nothing has been persisted.
    - `postId` string, uuid, required: The draft that was placed.
    - `publishAt` string, date-time, required: The ISO 8601 UTC instant this post will fire at.
    - `accountIds` string[], required: Every resolved account this post goes to. The bulk scheduler always cross-posts, so this is the same list for every entry.
    - `status` 'scheduled' | 'failed': `scheduled` when timers were created, `failed` when the underlying scheduler rejected this post. Absent on a dry run, since nothing was attempted.
    - `error` string: Present only when `status` is `failed`; explains why that post was rejected.
  - `skipped` SkippedEntry[], required: Posts that were not placed. Always check this. An empty array means everything you asked for was scheduled.
    - `postId` string, uuid, required: The post that was skipped.
    - `reason` string, required: Why it was skipped, typically that it is not a draft or not yours, or that it fell past the 50-posts-per-call limit, in which case simply call again.
  - `plan` PlanSummary, required: How the scheduler laid the posts out.
    - `timezone` string, required: The timezone the wall-clock times were interpreted in.
    - `perDay` integer, required: Posts placed per day. This is `minPerDay` clamped to the number of available slots.
    - `days` integer, required: How many distinct calendar days the plan spans.
    - `slotsUsed` string[], required: The specific `HH:MM` times used each day, in ascending order.
  - `dryRun` boolean: Present and true only when the request set `dryRun`. Its absence means timers were actually created.

## Other responses

- `400`: The request body or query string failed validation. `error.message` names the first problem found.
- `401`: No bearer token was sent, or the token is unknown, revoked or malformed. Run the device login again to obtain a new token.
- `402`: The plan behind this token does not include the capability this operation needs. `error.details.requiredPlan` names the tier that does. This is never a token problem, so do not retry without changing the plan.
- `404`: The named group does not exist.
- `422`: The request is well-formed but cannot be planned: no accounts resolved, more than 10 accounts in the group, no posting times available, or `minPerDay` larger than the number of time slots. The message says which.

---

[API](https://skmtc.dev/juzpost/apis/juzpost-rest-api.md) · [All operations](https://skmtc.dev/juzpost/apis/juzpost-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/juzpost/apis/juzpost-rest-api/revisions/19296da24f95?raw)
