Notion Backup Tool
Automated Notion workspace backup with version history and point-in-time restore. Indie-priced under Rewind. Weekend MVP guide + prompts.
Notion is the second brain for an estimated 30M+ users and 20K+ paying teams. It holds meeting notes, product specs, customer contracts, HR docs, and the CEO's half-finished book. Notion's own backup story is: "You can export a workspace to Markdown and HTML" — manually, one workspace at a time, with attachments that often fail on large workspaces. There is no scheduled backup, no version history across time, no granular restore of a single deleted page. People lose data in Notion routinely. A VA accidentally drags an entire database into the trash, past the 30-day recovery window. A guest user deletes 200 pages before revoking access. A Zapier integration goes rogue and overwrites content. A workspace admin clicks the wrong "delete forever" dialog on a Friday. In all these cases, Notion's support tells you "we cannot recover that." Teams have lost weeks of work.
Built for Developers, Solo Founders.
Suggested stack: Next.js 14 App Router, Supabase, Cloudflare R2 (primary storage), Notion API + Upstash QStash, Diff algorithm, Stripe Billing + Resend. Weekend scope: about 8 hours.
The Problem
Notion is the second brain for an estimated 30M+ users and 20K+ paying teams. It holds meeting notes, product specs, customer contracts, HR docs, and the CEO's half-finished book.…
The Solution
A Notion-focused backup SaaS. User connects their Notion workspace via OAuth; sets a schedule (hourly, daily, weekly); picks a destination (managed storage, S3, Google Drive…
Market Research
Rewind.io raised $65M Series A+ at ~$150M valuation (2021–2022) covering Shopify, Notion, GitHub, Trello, etc. Category validation at unicorn trajectory.
Competitive Landscape
Rewind.io — Category leader. Backs up Notion, Shopify, Slack, GitHub, HubSpot, Trello in one bundle. $65M funding, ~$150M valuation. Priced for teams who need the whole stack…
Business Model
Free ($0) — 1 workspace, weekly backup, 7-day retention, manual restore via download
Recommended Tech Stack
Next.js 14 App Router — Admin: workspace list, snapshot calendar, diff viewer, restore UI. Notion OAuth flow. Stripe Checkout. Server Actions for manual "backup now" and restore…
AI Prompts to Build This
Copy these build prompts into Claude, Cursor, or your AI coding tool. Create a free account to unlock the full research behind them.
1. Notion OAuth + Schema
Create a Next.js 14 App Router app for Notion backup. Supabase backend, TypeScript, Tailwind. Notion OAuth flow: 1. Register a Notion public integration at https://www.notion.so/my-integrations (Public type). 2. /api/notion/oauth route handles the callback; exchanges code for access_token via POST https://api.notion.com/v1/oauth/token with Authorization: Basic base64(clientId:clientSecret). 3. Store access_token + bot_id + workspace_id on workspaces table, scoped to the authenticated user. Schema: - users(id, email, stripe_customer_id, plan ENUM[free,solo,team]) - workspaces(id, user_id, notion_workspace_id, notion_access_token_encrypted, name, icon_url, schedule ENUM[hourly,daily,weekly], destination ENUM[managed,s3,drive], destination_config JSONB, created_at) - snapshots(id, workspace_id, started_at, completed_at, status ENUM[running,completed,failed], page_count, block_count, bytes_stored, root_hash) - snapshot_blocks(snapshot_id, block_id, page_id, content_hash, position) — no object data here - block_content(content_hash PRIMARY KEY, r2_key, bytes, created_at) — dedup table - restore_jobs(id, snapshot_id, target ENUM[new_workspace,in_place_page], status, log) RLS: user_id scoping on everything. Encrypt notion_access_token at rest using Supabase Vault. Routes: - /dashboard (workspaces list) - /workspace/[id] (calendar of snapshots, recent changes) - /snapshot/[id]/diff (vs prior snapshot) - /snapshot/[id]/restore (destination picker) - /connect (OAuth start)
2. Incremental Backup Worker
Implement the incremental backup worker. This is the product's cost moat. Upstash QStash schedule fires on each workspace's cron. Worker flow: 1. Create a snapshot row with status='running'. 2. Use Notion API /v1/search with filter=page to enumerate all pages in the workspace. Respect rate limit (3 req/s): use bottleneck or p-limit. 3. For each page, fetch /v1/blocks/{page_id}/children recursively (Notion nests arbitrarily; depth-first, max depth 5 for MVP). 4. For each block: a. Compute content_hash = sha256(JSON.stringify(canonicalBlock(block))) — canonicalize by sorting keys, stripping timestamps. b. If content_hash exists in block_content table: reuse, increment reference count. c. Otherwise: upload JSON to R2 at tenants/{tid}/blocks/{content_hash}.json, insert block_content row. d. Insert snapshot_blocks row linking snapshot_id → content_hash. 5. Compute page_hash = hash(sorted child block hashes). Compute root_hash from all page hashes. 6. Update snapshot with page_count, block_count, bytes_stored, root_hash, status='completed'. 7. If root_hash unchanged from prior snapshot, mark this snapshot as 'identical' — still record it but no new storage. Rate limit safety: - Bottleneck: max 3 req/sec, max 180 req/min. - On 429: exponential backoff starting at 2s, max 60s. - Persist cursor in snapshots.progress_json so a restart resumes without duplication. Error handling: - Archived/trashed pages: include in snapshot with archived=true flag; restore UI can show them separately. - Deleted-and-purged pages (>30 days): compare to prior snapshot; if missing, flag as "permanently deleted" and still available from last backup. Emit progress every 10 pages to Supabase realtime channel: {snapshot_id, pages_done, pages_total} — UI shows live progress bar.3. Restore Flow + Diff Viewer
Build the restore flow and diff viewer. These are the features people pay for. Diff viewer (/snapshot/[id]/diff): 1. Load current snapshot + prior snapshot. 2. Compare by block_id: added (in current, not prior), removed (in prior, not current), modified (content_hash differs). 3. Group by page. For each page, show: "+3 -1 ~2 blocks" summary. 4. Click page to expand: side-by-side diff using react-diff-viewer-continued, rendering the JSON block structure as readable markdown. 5. Filter: "Show only modified pages." 6. Diff is expensive if many blocks; paginate per-page lazily. Page-level restore: 1. User clicks "Restore this page to version X" on a diff view. 2. Server fetches all blocks for the chosen (snapshot, page_id) from R2 via content_hash table. 3. Options: a. Restore in-place: use Notion API PATCH /v1/blocks/{page_id} to update title, then replace children via DELETE existing blocks then POST children from snapshot. Warning: this is destructive; require double-confirmation. b. Restore as new page: create new sibling page with suffix "(restored from YYYY-MM-DD)" and POST children. Default, safest. 4. Track restore_jobs row; emit progress; notify via Resend on completion. Full workspace restore (Team tier only): 1. Pick snapshot. 2. Option A: restore into the original workspace (dangerous, requires typed confirmation of workspace name). 3. Option B: export as downloadable Notion-import ZIP (Markdown + attachments) — user can then import into a fresh workspace via Notion's native Import. 4. Log every restore action to audit_log for compliance.