• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
lightweight

lightweight

todoSweeper

Public
Like
todoSweeper
Home
Code
11
.claude
1
backend
6
frontend
4
shared
3
.vtignore
AGENTS.md
CHANGELOG.md
CLAUDE.md
README.md
deno.json
H
main.http.tsx
Environment variables
15
Branches
2
Pull requests
Remixes
1
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
2/7/2026
Viewing readonly version of main branch: v432
View latest version
README.md

Notion Todo Sync

Automatically syncs Notion checkbox todos to a centralized database with intelligent owner resolution, relation mapping, and AI-powered summaries.

Table of Contents

  • Quick Start
  • How It Works
  • Property Mapping
  • Owner Resolution
  • Other People Resolution
  • Due Date Resolution
  • Relation Mapping
  • Configuration Reference
  • Advanced Topics

Quick Start

Required Environment Variables

VariableDescription
NOTION_API_KEYYour Notion integration token (get one here)
TODOS_DB_IDDatabase ID where todos are synced (32-char ID without hyphens)

Required Database Properties

Your Todos database needs these properties at minimum:

Property NameTypePurpose
NameTitleThe todo text (cleaned up by AI)
Source block IDRich textUnique identifier to prevent duplicates

Setup Steps

  1. Create a Notion integration and get the API key
  2. Create a database with the required properties above
  3. Share the database with your integration
  4. Set the environment variables
  5. Test: POST /tasks/todos?hours=1

That's it! Additional properties (Owner, Due date, Projects, etc.) are optional and auto-detected.


How It Works

Notion pages → Search for todos → Blob storage → Sync to database
  1. Search: Scans recent pages for checkbox (to_do) blocks
  2. Extract: Captures text, @mentions, dates, page links, and surrounding context
  3. Store: Saves to blob storage with sync tracking
  4. Sync: Creates/updates pages in your Todos database with AI-powered summaries

What gets captured from a todo:

  • Block text → Name (polished by AI)
  • @mentions → Owner and Other People
  • Dates in text → Due Date (AI interprets which is the deadline)
  • Page links → Relations (auto-mapped to matching databases)
  • Preceding heading → Owner context (if heading matches a contact name)
  • Checkbox state → Status (if configured)

Health UI

Visit /api/health or the root URL to see a dashboard showing:

  • Connection status: Notion API and OpenAI connectivity
  • Property mappings: Which properties are configured and whether they exist in your database
  • Relations discovered: What relation properties were found and how many pages each targets
  • Owner/Status property types: Whether Owner is people or relation type, Status property detection

Use this to verify your setup is working and debug property mapping issues.


Property Mapping

The system maps todo data to your database properties. Properties are matched by name.

Default Property Names

KeyDefault NameRequiredPurpose
nameNameYesTodo text (title property)
sourceBlockIdSource block IDYesUnique ID for deduplication
ownerOwnerNoPerson responsible for the todo
otherPeopleOther peopleNoOther people mentioned
dueDateDue dateNoWhen it's due
status(disabled)NoCheckbox sync (Done/Not started)
projects(disabled)NoProject relation
sourceBlockUrlSource block URLNoLink to original block
sourcePageUrlSource page URLNoLink to source page
sourceLastEditedTimeSource last edited timeNoWhen source was edited
sourceAuthorSource authorNoWho created the block
sourceTextSource textNoRaw block text
linksLinksNoURLs found in block

Overriding Property Names

If your database uses different property names, override with environment variables:

TODOS_PROP_{KEY}=Your Property Name

Examples:

TODOS_PROP_DUE_DATE=Deadline # Use "Deadline" instead of "Due date" TODOS_PROP_OWNER=DRI # Use "DRI" instead of "Owner" TODOS_PROP_STATUS=Status # Enable status sync (disabled by default) TODOS_PROP_PROJECTS=Projects # Enable project linking (disabled by default)

To disable a property: Set it to empty string:

TODOS_PROP_OTHER_PEOPLE= # Don't sync other people

Property Type Detection

Owner and Other People can be either:

  • People type: Links to Notion workspace users
  • Relation type: Links to pages in another database (e.g., Contacts)

The system auto-detects the type from your database schema and adjusts matching behavior accordingly.


Owner Resolution

Owner is determined using a strict priority order. The first match wins.

Priority Order

┌─────────────────────────────────────────────────────────────┐
│ Priority 1: HEADING MATCH                                   │
│   Does the preceding heading match someone in owner DB?     │
│   Example: "### Alex" matches "Alex Johnson" in Contacts    │
├─────────────────────────────────────────────────────────────┤
│ MATCH → Use heading match (most reliable)                   │
│ NO    → Continue to Priority 2                              │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Priority 2: @MENTION                                        │
│   Is there an @mention in the todo?                         │
│   Example: "@Jane should review the PR"                     │
├─────────────────────────────────────────────────────────────┤
│ YES → Use first @mention                                    │
│ NO  → Continue to Priority 3                                │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Priority 3: AI EXTRACTION                                   │
│   AI extracts owner from todo text patterns                 │
│   Example: "Taylor to call Jordan" → Taylor is owner        │
├─────────────────────────────────────────────────────────────┤
│ Name extracted → Validate against owner database            │
│   FOUND → Use matched page/user                             │
│   NOT FOUND → Owner = null (prevents hallucination)         │
└─────────────────────────────────────────────────────────────┘

Heading Proximity Rules

Headings only apply to todos directly beneath them. Any intervening block resets the heading context.

Heading applies:

### Alex
- [ ] Todo 1              ← owner context: "Alex"
- [ ] Todo 2              ← owner context: "Alex"

Paragraph resets heading:

### Alex
Some intro text here
- [ ] Todo 1              ← owner context: null (reset by paragraph)

Empty line resets heading:

### Alex

- [ ] Todo 1              ← owner context: null (reset by empty line)

This prevents distant headings from incorrectly matching todos far below them.

People vs Relation Type

Owner TypeMatches Against
PeopleNotion workspace members only
RelationAny page in target database

Recommendation: Use Relation type pointing to a Contacts database. It's more flexible and works with both workspace members and external contacts.


Other People Resolution

Other People captures everyone mentioned in a todo except the owner.

Sources

  1. @Mentions: All @mentions (except the resolved owner) are added
  2. AI-Extracted Names: Names mentioned in text (only when Other People is a relation type)

AI-Extracted Name Matching

When Other People is a relation property, AI extracts names from the todo text:

"Call Kevin about the project" → AI extracts "Kevin"

Matching rules:

  • Exact match first (case-insensitive)
  • Prefix match fallback: "Kevin M" matches "Kevin McIntyre"
  • Skipped if ambiguous (multiple matches)
  • Skipped if already the owner or an @mention

Due Date Resolution

AI determines the due date from your todo content.

How It Works

  1. AI examines all dates mentioned in the todo
  2. AI picks the one that looks like a deadline
  3. If no date in text, uses DEFAULT_DUE_DATE setting
  4. AI always returns a date (never leaves it blank)

Default Date Options

Set DEFAULT_DUE_DATE environment variable:

ValueMeaning
todayDue today (default)
tomorrowDue tomorrow
one_weekDue 7 days from now
end_of_weekDue next Friday
next_business_dayDue next weekday

Relation Mapping

Todos are automatically linked to related pages (projects, tags, etc.) using these strategies.

Strategy 1: Explicit Matches (All Relations)

Works for any relation property.

1a: @Mentions If the todo @mentions a page that exists in a relation's target database, it's linked.

- [ ] Review [[Project Alpha]] specs
→ Linked to "Project Alpha" in Projects

1b: Heading Match If the preceding heading exactly matches a page name in a relation's target database, it's linked.

### Project Alpha
- [ ] Review the specs
→ Linked to "Project Alpha" in Projects

Strategy 2: Contextual Matches (Projects Only)

These strategies only apply to the Projects relation. Other relations require explicit @mentions or heading matches.

2a: Source Page Relations If the source page has a relation property with matching values, those are inherited.

Meeting Notes page has Project = "Acme Redesign"
→ All todos on that page inherit "Acme Redesign"

2b: Source Page is Database Entry If the source page is a direct entry in the Projects database, todos link to that page.

Todo on "Project Beta" page (which is in Projects DB)
→ Linked to "Project Beta"

2c: Parent Page Traversal For nested pages, walks up the hierarchy (up to 3 levels) looking for 2a/2b matches.


Configuration Reference

Required Variables

VariableDescription
NOTION_API_KEYNotion integration token
TODOS_DB_IDDatabase ID for synced todos

Property Overrides

VariableDefaultDescription
TODOS_PROP_NAMENameTodo title property
TODOS_PROP_SOURCE_BLOCK_IDSource block IDDeduplication ID
TODOS_PROP_OWNEROwnerPerson responsible
TODOS_PROP_OTHER_PEOPLEOther peopleOther mentions
TODOS_PROP_DUE_DATEDue dateDeadline
TODOS_PROP_STATUS(disabled)Checkbox sync
TODOS_PROP_PROJECTS(disabled)Project relation
TODOS_PROP_LINKSLinksURLs found
TODOS_PROP_SOURCE_BLOCK_URLSource block URLLink to block
TODOS_PROP_SOURCE_PAGE_URLSource page URLLink to page
TODOS_PROP_SOURCE_LAST_EDITED_TIMESource last edited timeEdit timestamp
TODOS_PROP_SOURCE_AUTHORSource authorBlock creator
TODOS_PROP_SOURCE_TEXTSource textRaw text

Behavior Settings

VariableDefaultDescription
DEFAULT_DUE_DATEtodayFallback when no date in todo
MIN_BLOCK_WORDS5Skip todos with fewer words
BLOCK_STABILITY_MINUTES0Wait before syncing (cron only)
RECENT_PAGES_LOOKBACK_HOURS24How far back to search
CRONS_DISABLEDfalseDisable automatic sync

Search Settings

VariableDefaultDescription
SEARCH_BLOCK_TYPEto_doBlock type to search for
SEARCH_KEYWORDS(none)Additional keywords (comma-separated)

Security

VariableDescription
NOTION_WEBHOOK_SECRETAPI key for protecting endpoints
OPENAI_API_KEYFor AI summaries (optional, uses Val Town shared if not set)

Advanced Topics

Endpoints

EndpointMethodPurpose
/tasks/todos?hours=24POSTSearch + sync recent pages
/tasks/todo/searchPOSTSearch single page (webhook)
/tasks/todo/savePOSTSync all blobs to database
/api/pages/recent?hours=24GETList recently edited pages
/api/healthGETSystem health check

Cron Jobs

Three crons run independently:

  • todoSearch.cron (every 1 min): Scans recent pages for todos
  • todoSync.cron (every 1 min): Syncs blobs to database
  • cacheWarm.cron (every 1 min): Keeps health cache fresh

Set CRONS_DISABLED=true during setup to test via webhooks instead.

Sync Optimization

The system tracks sync state in blob storage:

  • First sync: Query + create = 2 API calls
  • No changes: Skip immediately = 0 API calls
  • Block updated: Direct update = 1 API call

This reduces API calls by 90%+ for stable todos.

Caching

Two caches with 1-minute TTL:

  • Health cache: Instant dashboard loads
  • Relation cache: Fast sync initialization

Property renames in Notion may take up to 1 minute to propagate.

Status Sync

When TODOS_PROP_STATUS is set:

  • Checked checkbox → "Done" (or "Complete", "Completed", "Finished")
  • Unchecked → First non-done option (e.g., "Not started", "To Do")

Supports Status, Select, and Checkbox property types.

Validation

Todos are validated during search:

  • Must have at least MIN_BLOCK_WORDS words (default: 5)
  • Shorter blocks are skipped as not meaningful

Project Structure

backend/
├── controllers/     # Business logic
├── services/        # External API integrations
├── routes/          # HTTP handlers
├── crons/           # Scheduled jobs
└── utils/           # Helpers
frontend/            # React dashboard
shared/              # Shared types

Known Limitations

Notion Timestamp Precision: Block edit times are rounded to the minute. If you edit a todo twice within 60 seconds, only the first edit syncs until a later edit. Checkbox toggles are explicitly compared to catch same-minute changes.

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.