Overview
The Notion integration allows you to link elections to Notion database pages, enabling bidirectional synchronization of election status and metadata. This keeps your Notion workspace up-to-date with election progress automatically.
Features
Bidirectional Sync
- IHNYC-RC-VOTE → Notion: Election status updates (In Voting → Done)
- Notion → IHNYC-RC-VOTE: Title and description updates
Status Updates
- Not Started: When election is upcoming
- In Voting: When election is open
- Done: When election is closed
- Write-back authority: Notion status is overwritten by scheduled sync based on
open_at/close_at, so manual status edits are not preserved.
Results Link
Automatically updates a “Results Link” property in Notion with the election results URL.
Setup
Prerequisites
- Notion Integration: Create a Notion integration
- Database Allowlist: Configure allowed databases
- Notion API Key: Set
NOTION_API_KEYsecret
Step 1: Create Notion Integration
- Go to https://www.notion.so/my-integrations
- Click ”+ New integration”
- Name it (e.g., “IHNYC RC Voting”)
- Select workspace
- Copy the Internal Integration Token
Step 2: Share Database with Integration
- Open your Notion database
- Click ”…” (three dots) → “Connections”
- Select your integration
- Grant access
Step 3: Configure Database Allowlist
See notion-setup for detailed instructions.
Quick Setup:
wrangler secret put NOTION_DATABASE_ALLOWLIST
# Enter: database_id_1:Database Name,database_id_2:Another DatabaseStep 4: Set API Key
wrangler secret put NOTION_API_KEY
# Enter your integration tokenLinking Elections
Via Admin UI
- Go to election management page
- Scroll to “Notion Integration” section
- Click “Link to Notion Page”
- Select database from dropdown
- Search for page (optional)
- Select page
- Configure:
- Status Property Name: Default “Voting Status”
- Results Link Property: Default “Results Link”
- Click “Link Election”
Via API
Endpoint: POST /admin/elections/:id/notion/link
Request:
{
"database_id": "abc123def456",
"page_id": "ghi789jkl012",
"status_property_name": "Voting Status"
}Response:
{
"success": true,
"election": {
"notion_database_id": "abc123def456",
"notion_page_id": "ghi789jkl012",
"notion_status_property_name": "Voting Status"
}
}Status Property Requirements
Property Type
The status property must be a Status property in Notion.
Required Options
The status property must have these options:
- “In Voting” (or custom value)
- “Done” (or custom value)
- “Not started” (optional but recommended; used for upcoming elections)
Default Values
- In Voting:
"In Voting" - Done:
"Done" - Not started:
"Not started"
Custom Values
You can configure custom values in election settings:
{
"notion_status_value_in_voting": "Active",
"notion_status_value_done": "Completed"
}Synchronization
Automatic Sync
A cron job runs every minute to synchronize:
- Status Updates: Updates Notion status based on election state
- Results Links: Updates results link when election opens/closes
- Title/Description: Pulls updates from Notion
Sync Direction
IHNYC-RC-VOTE → Notion
When:
- Election opens → Status: “In Voting”
- Election is upcoming → Status: “Not started”
- Election closes → Status: “Done”
- Results available → Results link updated
Frequency: Every minute (cron job)
Idempotency: Only syncs if last sync was > 1 minute ago. Status values are enforced based on election timing, so manual Notion status edits will be overwritten on the next sync.
Notion → IHNYC-RC-VOTE
When:
- Page title changes → Updates election title
- Page description changes → Updates election description
Frequency: Every minute (cron job)
Idempotency: Only updates if values actually changed
Manual Sync
Via Admin UI:
- Go to election management page
- Click “Sync with Notion”
- Status write-back follows the same timing rules (upcoming → “Not started”, open → “In Voting”, closed → “Done”)
Via API:
Endpoint: POST /admin/elections/:id/notion/sync
Response:
{
"success": true,
"synced": {
"title": "Updated Title",
"description": "Updated Description"
}
}Page Search
Searching Pages
When linking an election, you can search for pages:
Endpoint: GET /admin/notion/databases/:databaseId/pages?query=search+term
Response:
{
"pages": [
{
"id": "page_123",
"title": "Election: Board President 2024",
"url": "https://notion.so/..."
}
],
"next_cursor": null
}Search Behavior
- Searches page titles
- Case-insensitive
- Returns up to 20 results per page
- Supports pagination via
cursorparameter
Page Validation
Validating Pages
Before linking, the system validates:
- Page exists in the database
- Status property exists with correct name
- Status property is Status type
- Required options exist (In Voting, Done)
Endpoint: GET /admin/notion/pages/:pageId/validate?property_name=Voting+Status
Response:
{
"valid": true
}or
{
"valid": false,
"error": "The property 'Voting Status' is not a Status property"
}Results Link Property
Automatic Updates
When an election opens or closes, the system updates a “Results Link” property in Notion:
Property Type: URL
Value: {BASE_URL}/e/{election_id}/results
Example: https://vote.ihnyc-rc.org/e/elec_123/results
Configuration
The results link property name defaults to “Results Link” but can be customized.
Note: The property must exist in the Notion page and be a URL property type.
Title and Description Sync
Pulling from Notion
The system pulls title and description from Notion page properties:
- Name Property: Maps to election title
- Description Property: Maps to election description
Property Detection
The system looks for:
- Name: Property named “Name” (title or rich_text type)
- Description: Property named “Description” (rich_text or text type)
Update Behavior
- Only updates if values have changed
- Handles null/empty descriptions
- Preserves existing values if Notion has no value
Error Handling
Sync Errors
If sync fails:
- Error is logged in
notion_last_sync_error - Sync retries on next cron run
- Election continues to function normally
Common Errors
“Notion API error: 401”
- Invalid API key
- Integration not shared with database
“Notion API error: 404”
- Page not found
- Database not found
“Property not found”
- Status property doesn’t exist
- Property name mismatch
Troubleshooting
- Check API Key: Verify
NOTION_API_KEYis set correctly - Check Sharing: Ensure integration has access to database
- Check Properties: Verify property names match
- Check Options: Ensure status options exist
Unlinking Elections
Via Admin UI
- Go to election management page
- Scroll to “Notion Integration” section
- Click “Unlink from Notion”
- Confirm
Via API
Endpoint: DELETE /admin/elections/:id/notion/link
Response:
{
"success": true
}Note: Unlinking does not delete the Notion page, only removes the connection.
Database Schema
Elections Table Fields
notion_database_id TEXT, -- Allowed database ID
notion_page_id TEXT, -- Linked page ID
notion_page_url TEXT, -- Page URL (for display)
notion_status_property_name TEXT, -- Status property name
notion_status_value_in_voting TEXT, -- Value for "in voting" status
notion_status_value_done TEXT, -- Value for "done" status
notion_last_sync_at INTEGER, -- Last sync timestamp
notion_last_sync_error TEXT, -- Last sync error (if any)
notion_done_set_at INTEGER -- When "Done" status was setCron Job
Schedule
Runs every minute via Cloudflare Workers cron trigger.
Tasks
- Update Closed Elections: Set status to “Done”
- Update Open Elections: Set status to “In Voting”
- Update Results Links: Update URL property
- Pull Title/Description: Sync from Notion
Idempotency
- Only syncs if last sync was > 1 minute ago
- Prevents infinite loops
- Handles concurrent updates
Best Practices
Database Organization
- One Page Per Election: Create separate pages for each election
- Consistent Properties: Use same property names across pages
- Status Options: Keep status options consistent
- Results Links: Always include results link property
Property Naming
- Status Property: “Voting Status” (or consistent name)
- Results Link: “Results Link” (or consistent name)
- Name Property: “Name” (for title sync)
- Description Property: “Description” (for description sync)
Sync Management
- Let It Sync Automatically: Don’t manually sync unless needed
- Monitor Errors: Check sync errors in election details
- Fix Errors Promptly: Resolve property/option issues
- Test First: Test with one election before linking many
Limitations
Current Limitations
- One Page Per Election: Cannot link multiple pages
- Fixed Property Names: Must use “Name” and “Description”
- Status Type Only: Status property must be Status type
- No Custom Properties: Cannot sync custom properties
Future Enhancements
Potential improvements:
- Multiple pages per election
- Custom property mapping
- Two-way sync for more fields
- Webhook support (real-time sync)
See Also
- notion-setup - Database allowlist configuration
- api-reference - API endpoint documentation
- ops - General infrastructure documentation