Scheduling
Automatic vs custom schedules, time zones, and the refresh commands.
Find the schedule view at
/project/{projectId}/dashboard/scheduled-posts (also reachable from the
dashboard's Scheduled posts widget).
Automatic mode
Default. The social-distribution-plan workflow runs:
- On config change —
refresh-schedulefires automatically when any ofdistributionMode,willPublish,postsPerDay,schedule.times,contentLayerSourceIds, orgenerationModechange. - Nightly —
refresh-schedule-scheduledruns on cron0 3 * * *UTC.
Each run:
- Loads the project's timezone.
- Validates the connected social account.
- Loads the latest health snapshot (if any) to pick an account
"temperature" —
cold(1 slot/day at 10:00),warm(2 slots at 10:00 + 15:00),hot(3 slots at 10:00 + 15:00 + 19:00). - Computes slots for a dynamic horizon —
max(3 days, ceil(availableContent / slotsPerDay)). - Upserts
scheduled_postsrows for each slot. - Attaches ready content_containers to the earliest empty slots.
- Optionally requests content generation to fill remaining gaps when
generationMode = "automatic".
Custom schedule
Set distributionMode: "custom" and supply times as HH:MM in the
project timezone:
{
"distributionMode": "custom",
"schedule": {
"times": ["09:00", "13:00", "18:00"]
}
}Only 24-hour HH:MM entries are supported (no day-of-week syntax).
postsPerDay equals schedule.times.length; allowed range is 1–5.
Manual mode
distributionMode: "manual" — the planner creates zero slots. Use this
if you want to hand-pick every schedule in the UI.
Time zones
The project timezone determines slot computation. The planner converts
local HH:MM values to UTC before writing scheduled_posts.scheduled_at.
Changing the project timezone later does not shift already-scheduled
posts.
Refresh-schedule command
refresh-schedule runs social-distribution-plan immediately. Invoke
it from the layer's Commands panel after changing config if you
don't want to wait for the onConfigChange effect.
Recover overdue
recover-overdue runs every 5 minutes and re-slots any ready or
failed post whose scheduled_at has passed. YouTube posts (dormant
platform) are explicitly skipped.