Notion sync — drafts, publishing, and outage immunity
Per-route control over when Notion edits go live, plus drift detection, preview links, and multi-stage approval.
Linkfor's Notion sync lets you control when Notion edits go live and survives Notion outages without dropping visitors. Each route can operate in one of two modes — choose based on how much tolerance you have for a typo reaching the public.
1. Two sync modes
| Behavior | Auto | Manual |
|---|---|---|
| Notion edit goes live | After cache expires (≤70 min) | When you click Publish |
| Visitor render hits Notion | Yes (cached) | No |
| Survives Notion outage | Yes (snapshot fallback) | Yes (snapshot only) |
| Best for | Marketing pages, blog posts | Legal copy, pricing, anything with a high cost of typos |
Auto (default): Every visitor request fetches from Notion via the SWR cache. Linkfor also stores a snapshot of the response on each fetch. If Notion goes down, visitors are served the most recent snapshot from Postgres — the same content they were already seeing — so the route stays up with no manual intervention.
Manual: The visitor render never contacts Notion. It reads the most recent published snapshot directly from Postgres. Edits you make in Notion are invisible until you explicitly publish a new draft. This is the right mode for pages where an accidental edit should not be visible to visitors within the hour.
Switch the mode in the route-edit dialog under Settings → Sync mode.
2. Capture a draft
- Open the route-edit dialog from the site's Routes tab.
- Go to the Sync panel.
- Click Fetch from Notion.
Linkfor calls the Notion API immediately, stores the result as a draft
snapshot, and shows you the captured timestamp. The live content is not
affected — visitors keep seeing the previous publish until you explicitly
promote the draft.
3. Publish a draft
Click Publish draft in the Sync panel. The draft snapshot becomes live
and the previous live snapshot moves to archived. Archived snapshots are
retained for rollback — nothing is deleted.
In auto mode, publishing a draft also invalidates the Redis cache for that route, so the new content is served immediately without waiting for the SWR window to expire.
4. Schedule a publish
Instead of Publish draft, click Schedule publish and pick a future date and time. A cron job runs every five minutes and publishes any drafts whose scheduled time has passed. The Sync panel shows the pending schedule so you can cancel it before it fires.
5. Drift detection
A nightly job (4 AM UTC) compares Notion's last_edited_time for each
route against the route's published_at timestamp. When Notion has newer
edits, a rose-tinted banner appears in the route's Sync panel:
Notion was edited [time], after the last publish at [time].
From the banner you can:
- Acknowledge — dismiss the warning without taking action (useful if the Notion edit was a private comment or a page you chose not to promote).
- Fetch latest — capture a new draft immediately, then proceed through the normal publish flow.
The banner reappears the next night if the drift has not been resolved.
6. Preview links
Before publishing, you can share a draft with someone who does not have a Linkfor account.
- In the Sync panel, click Share preview on the draft row.
- Linkfor mints a signed URL with a seven-day expiry.
- Send the URL to your reviewer. They see the draft exactly as a visitor would — no login required.
Preview links are scoped to the specific draft snapshot. If you capture a new draft after sharing, the old preview link still points to the earlier version.
7. Approval flow (optional)
For routes where a second person must sign off before content goes live:
- Open Settings → Sync mode on the route and enable Require approval before publish.
- The Publish draft button is replaced by Request approval.
- Fill in the reviewer's email address and submit.
- The reviewer receives an email with a one-click approval link (signed, seven-day expiry). Clicking the link both approves and publishes the draft in one step.
The approval email requires RESEND_API_KEY and RESEND_FROM_EMAIL env vars
in your deployment. Without them, the approval flow still works — the signed
link is logged to the server console — but no email is sent. See RUNBOOK.md
for the full env-var reference.
If the reviewer's link expires before they act, you can cancel the pending request and issue a new one from the Sync panel.
8. Rollback
The route-edit dialog's History section lists the last 30 archived snapshots with their publish timestamps.
- Find the snapshot you want to restore.
- Click Restore on that row.
The selected snapshot becomes the new live. The previously live snapshot
moves to archived. Nothing is lost — the full history remains available.
9. Outage immunity
If Notion is unreachable, both auto and manual mode serve the most recent live
snapshot from Postgres. Visitors see slightly older content rather than a 5xx
error. Linkfor emits a console.warn each time this fallback fires; the
Sentry integration captures it so you can track how often Notion is degraded
without relying on Notion's own status page.
10. Image-asset mirror (advanced)
Notion's image URLs expire after roughly one hour. In auto mode the SWR cache refreshes the record map before URLs expire under normal load, but on a cold start after an extended outage a visitor may see broken images if the cached snapshot contains stale image URLs.
To mirror image assets to Cloudflare R2 on every snapshot capture, set the
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, and R2_BUCKET
env vars, then toggle Mirror image assets on the site's Settings page.
Mirrored images are served from your R2 bucket and do not expire.