# Scheduled publishing on static sites: webhooks, rebuilds, and a cron fallback

Published: 2026-10-21 | Updated: 2026-10-21 | URL: https://blog.genko.me/en/scheduled-publishing-static-site

> Why a scheduled CMS entry does not update static HTML by itself, using genko.me's real publishing events. Choose rebuilds for Astro and Next.js static export, revalidation for ISR, and a cron fallback for missed notifications.

Scheduled publishing needs a second step on a static site: rebuild the site or invalidate its cached data when the CMS makes the entry available. genko.me sends an `entry.published` webhook when its scheduler confirms publication, which can trigger that step. A periodic job provides a recovery path when a notification or build fails.

This article was written by the operator of genko.me. The [scheduled publishing guide](https://docs.genko.me/en/guides/scheduled-publishing), live documentation, delivery API, scheduler, and SDK implementation were checked on October 21, 2026.

## The CMS schedule does not rewrite your HTML

Suppose you build at 8:00 and schedule a post for 9:00. The delivery API excludes the post at build time, so the generated listing has no link to it and the build may contain no detail page. Making the post available through the API at 9:00 does not change those files.

There are three separate points in the publishing process:

| Point | What it means | Responsible system |
| --- | --- | --- |
| Scheduled start | The delivery API can return the entry | CMS |
| Update begins | A rebuild or revalidation is requested | Webhook receiver or job |
| Website updates | Visitors receive the new output | Site and hosting environment |

Only the first is defined by the CMS schedule alone. Notification delay, build queues, deployment duration, and site caches affect the rest. “Available through the API at 9:00” does not promise that every page will have updated at exactly 9:00.

ISR also needs a trigger. If an expired cache is refreshed on the next request, it can sit untouched while nobody visits. Nor does revalidation replace the page already open in a visitor's browser. Treat API availability, cache refresh, and browser navigation as separate operations.

## genko.me sends a webhook for scheduled publication

As the [scheduled publishing guide](https://docs.genko.me/en/guides/scheduled-publishing) describes, the scheduler confirms due entries and then sends an `entry.published` webhook. The payload, documented in the [webhook guide](https://docs.genko.me/en/guides/webhooks), includes the event, workspace, API, entry, and notification time. A workaround based on the assumption that scheduled publication has no webhook is unnecessary.

API availability does not wait for that scheduler pass. The delivery query checks the current time against the publication window, including due entries whose stored status is still scheduled. After the end time, normal list requests exclude the entry and normal detail requests return 404.

The scheduler normally runs every 60 seconds. Its webhook therefore need not arrive at the precise scheduled instant. The entry's `publishedAt` is set to the scheduled start, rather than the time the scheduler happened to run. See the [publishing-window documentation](https://docs.genko.me/en/guides/scheduled-publishing).

A scheduled publication in the main language produces this shape. The ID and timestamp are illustrative:

```json
{
  "event": "entry.published",
  "workspace": "your-workspace",
  "api": "blog",
  "entry": {
    "id": "abcDEF123456",
    "status": "published"
  },
  "triggeredAt": "2026-10-21T00:00:42.000Z"
}
```

The webhook contains neither the article body nor its scheduled start. Do not use `triggeredAt` as the publication date. Fetch the current delivery API data when rebuilding or rendering.

Notifications for an additional language include a top-level `lang`. Entry IDs are shared across languages, so a receiver that only updates the default-language route can leave translated pages stale.

## Rebuild Astro and Next.js static exports

For a site served as static files, a hosting build hook is a straightforward destination. Register it directly if it accepts genko.me's POST requests. Put a receiver in between when you need signature verification, event filtering, or control over concurrent builds.

An Astro rebuild reruns its build-time content fetches. This example assumes the configured `client` from the [Astro getting-started guide](https://docs.genko.me/en/frameworks/astro/getting-started):

```astro
---
// src/pages/blog/[id].astro
import { client } from "../../lib/genko";

export async function getStaticPaths() {
  const posts = await client.blog.getAll();
  return posts.map(post => ({
    params: { id: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
---
<html lang="en">
  <head><title>{post.title}</title></head>
  <body>
    <h1>{post.title}</h1>
    <div set:html={post.body} />
  </body>
</html>
```

Here, `blog` is a list API with a text `title` and rich-text `body`. A build after the scheduled start fetches entries that are currently available. Keep the delivery API key in the build environment. `getAll()` paginates internally, avoiding a build that accidentally generates detail pages for only the first response page.

Include the listing, pagination, homepage, category pages, RSS, and sitemap in the update plan. Creating a detail page while leaving every incoming link unchanged makes the new post hard to discover.

Next.js with `output: 'export'` likewise requires a new build and deployment to replace exported files. A static hosting directory does not provide a running Next.js revalidation endpoint. Check the [official static export guide](https://nextjs.org/docs/app/guides/static-exports) against your deployment architecture.

## Revalidate when a Next.js server owns the cache

With a running Next.js server, invalidate the cached data or paths used by the affected pages. The choice follows your hosting architecture, rather than whether the CMS calls the operation scheduled publishing.

| Architecture | Update mechanism | Check |
| --- | --- | --- |
| Static Astro pages | Rebuild and deploy | New routes and listings are generated |
| Next.js static export | Rebuild and deploy | Revalidation needs a separate server environment |
| Cached Next.js server rendering | Invalidate tags or paths | Tags match those assigned during fetching |
| Uncached server rendering | Fetch on the next request | Upstream proxies do not retain stale output |

[`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag) targets tagged data. [`revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath) targets a page or layout path. The `'max'` tag profile permits stale content while refreshing; `{ expire: 0 }` makes the next fetch wait for fresh data. These calls should not be interpreted as eagerly regenerating every affected URL at the moment the webhook arrives.

For the full receiver, signature verification, and fetch-tag setup, use [Reflect CMS changes in Next.js with webhooks and revalidation](https://blog.genko.me/en/nextjs-webhook-revalidate). This article focuses on scheduling and recovery rather than repeating that implementation.

## Include manual publishing and scheduled removal

Registered genko.me webhooks receive the API's events. Filter them at the receiver if necessary. The [webhook guide](https://docs.genko.me/en/guides/webhooks) distinguishes these operations:

| Operation | Event | Website action |
| --- | --- | --- |
| Create an entry, including a directly published entry | `entry.created` | Refetch public data |
| Save, manually publish, or return to draft | `entry.updated` | Regenerate for the new state |
| Delete | `entry.deleted` | Remove stale details and links |
| Confirm scheduled publication | `entry.published` | Add details and listing links |
| Confirm time-based unpublishing | `entry.unpublished` | Remove expired content |
| Send a test | `webhook.test` | Check reception; `entry` is `null` |

Listening only for `entry.published` misses manual publishing. Requiring `entry.status === 'published'` misses transitions that should remove an existing page. Saving unpublished changes to an already published entry does not itself send a webhook.

Scheduled removal deserves the same attention as publication. An API 404 does not erase HTML already deployed to your site. Decide what happens if the removal build fails and the old deployment stays online. Content with a strict expiry may need a request-time availability check rather than relying exclusively on a successful rebuild.

## Use cron to recover from missed updates

genko.me webhook delivery times out after 10 seconds and has no automatic retry. Failed notifications can be resent manually from delivery logs. A periodic job reduces dependence on someone noticing a failure during an unattended release.

For a static site, the simplest fallback requests the same build every ten minutes. This example assumes the cron environment securely provides `SITE_BUILD_HOOK` and that the destination accepts POST requests:

```cron
*/10 * * * * curl --fail --silent --show-error --request POST "$SITE_BUILD_HOOK"
```

Keep the actual hook URL out of article examples and repositories. A cron process may not inherit your interactive shell's environment, so configure its secrets explicitly in the job runner.

This intentionally rebuilds even if nothing changed. To avoid unnecessary builds, compare a complete public snapshot with the last successful snapshot before requesting a build. Include IDs and update metadata, and account for deletions and scheduled removals. Looking only at the newest entry's update timestamp can miss a removed article. Never interpret an API failure as an empty site.

Ten-minute polling adds up to a polling interval before work begins, plus any queue and deployment time. It is not a ten-minute guarantee when the job itself can fail. If a receiver queues builds, let both cron and webhooks use the same queue, coalesce requests, and prevent an older build from replacing newer output.

For Next.js, a periodic job can call an authenticated internal revalidation endpoint instead. Give that job its own authentication and invoke the shared update function; there is no need to manufacture a genko.me webhook signature.

## Verify the entire scheduled transition

Schedule a test entry a few minutes ahead. Record the selected time zone, check when the delivery API starts returning it, inspect the webhook delivery, and confirm the resulting deployment or revalidation at the public URL. An HTTP 200 from a receiver does not prove a queued build succeeded.

Then schedule its end and verify removal from detail pages, listings, feeds, and homepage sections. Resend the same notification to check duplicate handling. Finally, check that the periodic path can restore current content without a webhook. These checks cover the operational gaps between a correct CMS schedule and a correct public website.

## FAQ

### Does genko.me send a webhook when a scheduled post goes live?

The scheduler sends `entry.published` when it confirms publication. The delivery API can expose the post from its scheduled start, before that notification arrives.

### Can I schedule a build for exactly the publication time?

That can work, but it must handle changed schedules and failed jobs. A CMS webhook as the main trigger, with a periodic recovery job, avoids maintaining a separate build schedule for every post.

### Is the ISR expiry interval enough?

It depends on the delay you can accept. If refresh starts on the next visit, cache expiry does not promise an update at the scheduled instant. Add webhook revalidation when that difference matters.

### Does scheduled unpublishing remove deployed static pages?

Previously generated files remain until the site updates them. Handle `entry.unpublished`, update every affected surface, and provide recovery for failed notifications and deployments.
