Webhook handlers for Notion integrations, organized into focused modules.
/backend/routes/tasks/
├── _tasks.routes.ts # Main router + catch-all
├── webhook.routes.ts # Production webhook endpoints
├── debug.routes.ts # Debug/test endpoints
└── [new-feature].routes.ts # Future endpoints (auto-mounted safely)
To add new /tasks
endpoints:
- Create a new route file (e.g.,
feature.routes.ts
) - Import and mount it in
_tasks.routes.ts
BEFORE the catch-all - Routes are automatically protected from the catch-all footgun
Updates a Notion page's URL property with a glimpse URL.
Authentication: Requires X-API-KEY
header with NOTION_WEBHOOK_SECRET
value.
Request Body:
{ "data": { "id": "page-id-here" } }
Alternative payload structures supported:
{ "id": "page-id-here" }
{ "page_id": "page-id-here" }
Success Response (200):
{ "success": true, "message": "Page URL updated successfully", "pageId": "page-id-here", "url": "https://your-domain.com/glimpse/page-id-here", "timestamp": "2024-01-01T00:00:00.000Z" }
Error Responses:
400 - Missing page ID:
{ "success": false, "error": "Page ID is required in webhook payload" }
400 - Missing host header:
{ "success": false, "error": "Host header is required" }
500 - Notion API error:
{ "success": false, "error": "Failed to update Notion page with URL", "details": "Notion API error message" }
Testing:
curl -X POST https://your-val.web.val.run/tasks/url \ -H "X-API-KEY: your-notion-webhook-secret" \ -H "Content-Type: application/json" \ -d '{"data": {"id": "your-page-id"}}'
Test webhook receiver - logs payload and returns success. Not for production use.
Authentication: Requires X-API-KEY
header with NOTION_WEBHOOK_SECRET
value.
Response:
{ "success": true }
Test endpoint for webhook authentication.
Authentication: Requires X-API-KEY
header with NOTION_WEBHOOK_SECRET
value.
Success Response (200):
{ "success": true, "message": "Webhook authentication successful", "timestamp": "2024-01-01T00:00:00.000Z" }
Debug endpoint to check webhook configuration.
No authentication required.
Response:
{ "hasSecret": true, "secretLength": 32 }