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

  1. IHNYC-RC-VOTE → Notion: Election status updates (In Voting → Done)
  2. 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.

Automatically updates a “Results Link” property in Notion with the election results URL.

Setup

Prerequisites

  1. Notion Integration: Create a Notion integration
  2. Database Allowlist: Configure allowed databases
  3. Notion API Key: Set NOTION_API_KEY secret

Step 1: Create Notion Integration

  1. Go to https://www.notion.so/my-integrations
  2. Click ”+ New integration”
  3. Name it (e.g., “IHNYC RC Voting”)
  4. Select workspace
  5. Copy the Internal Integration Token

Step 2: Share Database with Integration

  1. Open your Notion database
  2. Click ”…” (three dots) → “Connections”
  3. Select your integration
  4. 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 Database

Step 4: Set API Key

wrangler secret put NOTION_API_KEY
# Enter your integration token

Linking Elections

Via Admin UI

  1. Go to election management page
  2. Scroll to “Notion Integration” section
  3. Click “Link to Notion Page”
  4. Select database from dropdown
  5. Search for page (optional)
  6. Select page
  7. Configure:
    • Status Property Name: Default “Voting Status”
    • Results Link Property: Default “Results Link”
  8. 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:

  1. Status Updates: Updates Notion status based on election state
  2. Results Links: Updates results link when election opens/closes
  3. 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:

  1. Go to election management page
  2. Click “Sync with Notion”
  3. 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"
  }
}

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 cursor parameter

Page Validation

Validating Pages

Before linking, the system validates:

  1. Page exists in the database
  2. Status property exists with correct name
  3. Status property is Status type
  4. 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"
}

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

  1. Check API Key: Verify NOTION_API_KEY is set correctly
  2. Check Sharing: Ensure integration has access to database
  3. Check Properties: Verify property names match
  4. Check Options: Ensure status options exist

Unlinking Elections

Via Admin UI

  1. Go to election management page
  2. Scroll to “Notion Integration” section
  3. Click “Unlink from Notion”
  4. 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 set

Cron Job

Schedule

Runs every minute via Cloudflare Workers cron trigger.

Tasks

  1. Update Closed Elections: Set status to “Done”
  2. Update Open Elections: Set status to “In Voting”
  3. Update Results Links: Update URL property
  4. 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

  1. One Page Per Election: Create separate pages for each election
  2. Consistent Properties: Use same property names across pages
  3. Status Options: Keep status options consistent
  4. Results Links: Always include results link property

Property Naming

  1. Status Property: “Voting Status” (or consistent name)
  2. Results Link: “Results Link” (or consistent name)
  3. Name Property: “Name” (for title sync)
  4. Description Property: “Description” (for description sync)

Sync Management

  1. Let It Sync Automatically: Don’t manually sync unless needed
  2. Monitor Errors: Check sync errors in election details
  3. Fix Errors Promptly: Resolve property/option issues
  4. Test First: Test with one election before linking many

Limitations

Current Limitations

  1. One Page Per Election: Cannot link multiple pages
  2. Fixed Property Names: Must use “Name” and “Description”
  3. Status Type Only: Status property must be Status type
  4. 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