You're reading docs for Nextly Alpha. APIs may change between releases.

Content Releases

Content Releases

Group documents into a release and publish or unpublish them together, at a time you choose.

A release is a set of documents that go live at the same instant. A product launch touching a landing page, three blog posts and a pricing single becomes one thing you schedule, rather than five things someone has to publish in the right order at 9am.

Each member of a release is set to either publish or unpublish, so the same release can put new pages live and retire old ones in the same moment.

Availability

Built inNo plugin required
AdminA Releases section with a calendar view, and an "Add to release" action on documents
PermissionScheduling requires publish access. A key scoped only to publishing releases can schedule without holding a general read grant

What it is not

This is scheduled publishing, not an approval workflow. There are no review stages, transitions or assignees. If you need "editor submits, manager approves, then it goes live", that is not what a release does.

The shape of a release

StepWhat happens
CreateAn empty release with a name
Add membersEach member names a document and an action, publish or unpublish
ScheduleThe release is given a time. Blocking problems are checked first
ApplyOn a background-jobs pass after that time, every member's action is applied
CancelAn unapplied release can be cancelled

Scheduling stores a time; a job runner applies it

Scheduling records when a release is due. The work is done by a releases:drain sweep that runs as part of the background jobs pass, which happens only when something calls the jobs route.

Running the queue is where that is set up: the path, the shared secret the caller needs, and the two ways a scheduler ends up silently doing nothing. Wire it there once and every scheduled release depends on it.

With no scheduler configured, a release is never applied. Its due time passes and the sweep that would carry it out never runs. A scheduler configured but refused, by the wrong path or a missing secret, leaves you in the same place.

What that does not mean is that the content change is invisible. A read consults the same due-release rule the sweep does, so a scheduled draft can be revealed, and a takedown hidden, before anything has run. On a multi-instance deployment an instance other than the one that scheduled it can be up to 30 seconds behind, so treat this as "within seconds of the instant" rather than exactly at it.

As apply-due-releases.ts puts it, the read path can only make a due release LOOK applied. The sweep is what applies it for real, writing as the member's own author, and it is the only thing that advances the release's own state.

What a reader sees and what the stored rows hold are therefore two different questions while a release is waiting for the sweep, and the second one has more cases than the first: overlapping releases, a member whose author is gone, and a pass that applied some members before failing on another. If you integrate against the database directly rather than through Nextly, read apply-due-releases.ts before relying on release state.

A pass is bounded, which is why a due release is not always applied by the next tick: it claims at most 10 queued jobs and stops starting new ones after 20 seconds. The bounds and what they mean for a serverless timeout are on the jobs page.

The sweep can also defer work past its deadline while still reporting success. So a release is applied on the first pass that actually reaches and completes it, which is not always the first pass after its due time. With a backlog, or several expensive releases due together, expect it a pass or two later. Treat the scheduled time as "not before", not as "at".

Blocking reasons

Before a release can be scheduled, it is checked for problems that would make it fail half-way — a member pointing at a document that no longer exists, for example. Those are reported as blocking reasons so they can be fixed while there is still time, rather than surfacing as a partial publish at 9am.

An empty release has no blockers; there is nothing to go wrong.

One release covers every language

A member is the whole document. Adding a post to a release schedules that post in every language it has.

Per-locale scheduling is not supported: adding a member that names a single locale is refused, and the admin only offers the action from a document's default locale. Each language does have its own published state — see Localization — but that is publishing, not scheduling.