#!/usr/bin/env node /** * Update Notion page properties (for database pages) * Usage: update-page-properties.js [--type select|multi_select|checkbox|number|url|email|date] */ const https = require('https'); const NOTION_API_KEY = process.env.NOTION_API_KEY; const NOTION_VERSION = '2025-09-03'; if (!NOTION_API_KEY) { console.error('Error: NOTION_API_KEY environment variable not set'); process.exit(1); } function notionRequest(path, method, data) { return new Promise((resolve, reject) => { const requestData = JSON.stringify(data); const options = { hostname: 'api.notion.com', port: 443, path: path, method: method, headers: { 'Authorization': `Bearer ${NOTION_API_KEY}`, 'Notion-Version': NOTION_VERSION, 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(requestData) } }; const req = https.request(options, (res) => { let body = ''; res.on('data', (chunk) => body += chunk); res.on('end', () => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(JSON.parse(body)); } else { const error = JSON.parse(body); if (res.statusCode === 404 && error.code === 'object_not_found') { reject(new Error('Page not found. Make sure it is shared with your integration.')); } else if (res.statusCode === 401) { reject(new Error('Authentication failed. Check your NOTION_API_KEY.')); } else if (res.statusCode === 400 && error.code === 'validation_error') { reject(new Error(`Validation error: ${error.message}. Check property name and type.`)); } else if (res.statusCode === 429) { reject(new Error('Rate limit exceeded. Wait a moment and try again.')); } else { reject(new Error(`API error (${res.statusCode}): ${error.message || body}`)); } } }); }); req.on('error', reject); req.write(requestData); req.end(); }); } function formatPropertyValue(propertyType, value) { switch (propertyType) { case 'select': return { select: { name: value } }; case 'multi_select': // Value can be comma-separated: "AI,Leadership,Research" const tags = value.split(',').map(t => t.trim()); return { multi_select: tags.map(name => ({ name })) }; case 'checkbox': const boolValue = value.toLowerCase() === 'true' || value === '1'; return { checkbox: boolValue }; case 'number': return { number: parseFloat(value) }; case 'url': return { url: value }; case 'email': return { email: value }; case 'date': // Value can be "2024-01-15" or "2024-01-15,2024-01-20" for range const dates = value.split(',').map(d => d.trim()); return { date: { start: dates[0], end: dates[1] || null } }; case 'rich_text': return { rich_text: [{ type: 'text', text: { content: value } }] }; default: throw new Error(`Unsupported property type: ${propertyType}. Supported: select, multi_select, checkbox, number, url, email, date, rich_text`); } } async function updatePageProperties(pageId, propertyName, value, propertyType = 'select') { const properties = {}; properties[propertyName] = formatPropertyValue(propertyType, value); console.error(`Updating page: ${pageId}`); console.error(`Property: ${propertyName} (${propertyType})`); console.error(`Value: ${value}`); const result = await notionRequest(`/v1/pages/${pageId}`, 'PATCH', { properties }); return { id: result.id, url: result.url, updated: result.last_edited_time }; } // Main execution (async () => { try { const args = process.argv.slice(2); if (args.length < 3 || args[0] === '--help') { console.log('Usage: update-page-properties.js [--type ]'); console.log(''); console.log('Options:'); console.log(' --type Property type (default: select)'); console.log(''); console.log('Supported types:'); console.log(' select Single choice (e.g., "Complete")'); console.log(' multi_select Multiple choices, comma-separated (e.g., "AI,Leadership")'); console.log(' checkbox Boolean (e.g., "true" or "false")'); console.log(' number Numeric value (e.g., "1200")'); console.log(' url URL string'); console.log(' email Email address'); console.log(' date Date or range (e.g., "2024-01-15" or "2024-01-15,2024-01-20")'); console.log(' rich_text Text content'); console.log(''); console.log('Examples:'); console.log(' # Set status to Complete'); console.log(' update-page-properties.js Status Complete --type select'); console.log(''); console.log(' # Add multiple tags'); console.log(' update-page-properties.js Tags "AI,Leadership,Research" --type multi_select'); console.log(''); console.log(' # Set checkbox'); console.log(' update-page-properties.js Published true --type checkbox'); console.log(''); console.log(' # Set date'); console.log(' update-page-properties.js "Publish Date" 2024-02-01 --type date'); console.log(''); console.log(' # Set URL'); console.log(' update-page-properties.js "Source URL" "https://example.com" --type url'); process.exit(0); } const pageId = args[0]; const propertyName = args[1]; const value = args[2]; let propertyType = 'select'; for (let i = 3; i < args.length; i++) { if (args[i] === '--type' && args[i + 1]) { propertyType = args[i + 1]; i++; } } const result = await updatePageProperties(pageId, propertyName, value, propertyType); console.log(JSON.stringify(result, null, 2)); console.error(`\n✓ Page updated successfully`); console.error(` URL: ${result.url}`); } catch (error) { console.error('Error:', error.message); process.exit(1); } })();