findings
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.
Email trigger handlers for processing incoming emails.
pdf-attachments.ts
- Processes emails with PDF attachments and uploads them to Notion database pages
Email handlers follow the same separation of concerns as other parts of the application:
- Email Handlers: Extract data from email objects and call controllers
- Controllers: Business logic for processing email data
- Services: Pure API calls to external systems (Notion, etc.)
The pdf-attachments.ts
handler:
- Deduplication Check: Uses precise time difference comparison to prevent duplicate processing
- Receives emails with PDF attachments
- Creates child pages in the Notion database specified by
FINDINGS_TRANSCRIPTS_DB_ID
- Uploads each PDF to its own page using Notion's 3-step file upload API
- Continues processing even if individual uploads fail
- Returns detailed results for monitoring
The handler implements idempotency using precise time difference comparison:
- Hash Generation:
from + subject
(no time component) - Storage: Blob storage with key
findings--transcripts--email--{hash}
- Window: Same sender + subject within 3 minutes = duplicate
- Precision: Exact time difference calculation (not epoch blocks)
- Behavior: Duplicates are logged with time difference and skipped
- Updates: Legitimate emails (>3 minutes apart) update the stored timestamp
This prevents duplicate Notion records when email providers retry delivery while allowing legitimate re-sends after 3 minutes.
Each PDF attachment creates a page with:
- Name: Extracted interviewee name from filename (content in parentheses)
- Filename: Complete original filename from email attachment
- PDF: Uploaded file using Notion's file upload API
- Status: Upload progress tracking
- "Uploading...": Initial status when page is created
- "Uploaded": File successfully uploaded and attached to page
- "Upload error": File upload failed (page still exists for retry)
The handler extracts interviewee names using pattern matching:
- Pattern:
(Name)
- Content inside first parentheses becomes the page title - Example:
"Interview (John Doe) - 2025_01_01.pdf"
→ Name:"John Doe"
- Fallback: If no parentheses found, uses filename without extension
FINDINGS_TRANSCRIPTS_DB_ID
- Notion database ID where pages will be createdNOTION_API_KEY
- Notion API key for authentication
- Email Reception: Handler receives email with attachments
- PDF Filtering: Only processes PDF attachments (by extension or MIME type)
- Page Creation: Creates a new page in the target database for each PDF
- File Upload: Uses Notion's 3-step upload process:
- Create upload object
- Upload file contents
- Attach file to page
- Status Updates: Updates page status based on upload success/failure
- Error Handling: Continues processing even if individual steps fail
- Response: Returns detailed processing results