scheduled_publish_at date that nothing executed. The editor wrote it, the API answered 201, the row said “scheduled”, and the post never went live; the editor eventually dropped its schedule input because of that, so a writer who wanted a post out Monday at 8 had to leave for the CMS entry sidebar. Nor was there any view of what the account had planned: “what is going out this month” meant opening every item.
Both are fixed. Every surface that schedules a content item now writes the one scheduler that runs, and a calendar under Marketing > Content shows what it will do.
Scheduling that executes
The scheduler behind CMS scheduled publishing is a queue of publish actions keyed on the site entry (project, collection, entry slug), claimed by a cron on the minute the action falls due. A content item is scheduled by putting its entry on that queue, which has two consequences:- An item must be linked to a site collection first. The schedule is keyed on the entry, so an item with no
website_project_idandcms_collection_idhas nothing to schedule. Every surface answers409 not_linkedwith the link step named, never an inert row. Link the item from the editor’s Publish to dialog, or withcontent_link_to_cmsorcontent_update, then schedule. - A linked item that was never pushed to the site gets its entry created as a draft at schedule time, through the same mapping and validation a publish uses, with the site’s live signal off. Nothing goes live before the scheduled time, and the entry exists for the cron to flip.
status becomes published, published_at is set on the first go-live, and the scheduled_publish_at mirror is cleared because the schedule has run. A failure is retried; after five attempts the cron stops and the row stays visible as failed rather than disappearing. One pending row exists per entry, so scheduling again replaces the earlier time.
From the editor
A Schedule action appears in the sidebar’s Status section and in the header menu of a CMS-linked, saved item. The Schedule publish dialog takes a date and time in your local zone and a Then choice: the deploy targets the scheduler executes.
The dialog says what it will do: “The post stays off the site until this time, then publishes on the site’s schedule. Unsaved edits are saved with it.” Once scheduled, the sidebar, the toast and the Settings publishing card all read “Goes live at
<time> on the site’s schedule”, and Cancel schedule in the dialog clears it.
From the dashboard API
PUT /api/marketing/content/{id} is the editor’s own save, and it takes the whole item on every autosave, so scheduled_publish_at arrives on every request. The route reads intent from a change, not a presence:
- A
scheduled_publish_at(ISO, in the future) whose instant differs from the stored column is a schedule request. It is validated before any write: a past time or an unknowndeploy_targetis a 400, an unlinked item is409 not_linked, and neither writes anything. - The value the row already holds is an autosave echo and is ignored.
nullnever cancels. An editor tab opened before an agent scheduled the item would otherwise cancel that schedule on its next keystroke. The cancel is the explicitcancel_scheduled_publish: true.deploy_targetisproduction(default),staging,dev,noneorpublish.
note: "Fields were saved; the schedule was NOT set.", so a save is never mistaken for a schedule.
From the Olympus API and MCP
The content calendar
Marketing > Content > Calendar (/dashboard/marketing/content/calendar, marketing.content read) is a month grid and a day-grouped list of everything the account has planned or published. Days are wall-clock days in your browser’s zone, named at the top right, and the grid opens on your current month. Each day shows up to three items with a time and a title; more than three collapses to “+n more”. Clicking an item opens it in the editor, or the CMS entry when no content item is linked to a scheduled entry.
Four kinds of event, each with its own colour:
The header line is the account’s cadence: “12 published in the last 30 days, 3 scheduled, target cadence from memory not set”, with “2 failed publishes needing attention” added when there are any. The target is read from the Content department’s memory (the
content domain first, then marketing) when a line there states a publishing cadence: “publish 2 blog posts per week”, “one article a month”, “twice a week”. It is normalised to pieces per week and the line it was read from is shown under the summary, so you can see what the department believes the target is and correct the memory when it is wrong. Those counts are account-wide, not limited to the month on screen.
The calendar tells the truth about two tables. A pending scheduler row is the schedule, and the item’s own date for the same entry is dropped rather than shown twice. A completed row is dropped when the item already records its
published_at (one dot per publication) and otherwise shows as published from the scheduler. A row the cron gave up on shows as failed, never as pending. An item date with no row for its entry shows as recorded only, because nothing will publish it.The calendar API
GET /api/marketing/content/calendar?from=&to= feeds the page and is available to any signed-in user with marketing.content read. from and to are ISO datetimes or YYYY-MM-DD (a date-only to means the end of that day, UTC); both default to 45 days either side of now, and a window wider than 190 days is refused with a 400 rather than silently clipped.
kind is scheduled, published, failed or recorded_only; source says which table the event came from; executes is false only for recorded_only. Event ids are stable across reloads (item:<id>:published, item:<id>:recorded, action:<rowId>).
Related pages
- The Content Department — the writer whose memory the cadence target is read from
- Pre-Publish SEO Check — runs when the item is published, scheduled or not
- Scheduled Publishing — the same scheduler from the CMS entry sidebar, with the deploy behaviour in detail
- Webflow as the Content Source — what a scheduled publish does on a Webflow-backed project