This guide is for implementing webhook endpoints in the Glance Demo Platform Val that follow the established architectural patterns.
- Before implementing, identify which pattern to use:
- Data API Pattern (/api/, /views/): Controllers return data objects, routes handle HTTP
- Webhook Handler Pattern (/tasks/*): Controllers handle HTTP requests/responses directly
For /tasks/* routes, use the Webhook Handler Pattern.
- Study these files to understand the established patterns:
/backend/controllers/glimpse.controller.ts
- Example of webhook handler pattern/backend/routes/tasks/_tasks.routes.ts
- Existing webhook route structure/backend/services/notion.service.ts
- Service layer patterns/backend/routes/webhookAuthCheck.ts
- Authentication middleware
- For a new /tasks/url endpoint, you'll need:
- Controller function (handles HTTP directly)
- Service function (if new Notion API calls needed)
- Route definition
- Documentation updates
If new external API calls are required:
- Location: /backend/services/notion.service.ts
- Pattern: Add pure API functions that return {success, data/error, timestamp} format
- Example: updatePageUrl(pageId, url) function
- Responsibilities: Only handle external API calls, no business logic
- Location:
/backend/controllers/tasks.controller.ts
- Pattern: Webhook Handler Pattern - function takes Context, returns HTTP responses
- Function signature: export async function handleNotionWebhook(c: Context)
- Responsibilities:
- Parse HTTP request (c.req.json(), c.req.header())
- Extract and validate data from webhook payload
- Implement business logic
- Call service functions
- Log success/failure with detailed context
- Return HTTP responses with appropriate status codes (c.json(data, statusCode))
- Location:
/backend/routes/tasks/_tasks.routes.ts
- Pattern: Direct controller call
- Implementation:
app.post("/url", handleNotionWebhook)
- Update catch-all handler: Add new endpoint to
availableEndpoints
array
Main README (/README.md)
:
- Add endpoint to "Webhooks (Notion Integration)" section
- Follow format:
POST /tasks/url
- Description (requiresX-API-KEY
header)
Route README (/backend/routes/tasks/README.md)
:
- Add comprehensive endpoint documentation
- Include request/response examples
- Document error cases
- Add testing instructions
Controller README (/backend/controllers/README.md)
:
- Add controller function to examples
- Note that it follows webhook handler pattern
Service README (/backend/services/README.md)
:
- Add any new service functions to examples
- All
/tasks/*
routes automatically use webhook authentication - Requires X-API-KEY header with NOTION_WEBHOOK_SECRET value
- No additional auth setup needed in route
Support multiple payload structures:
// Try multiple common webhook payload structures
if (body.data?.id) {
pageId = body.data.id;
} else if (body.id) {
pageId = body.id;
} else if (body.page_id) {
pageId = body.page_id;
}
Use this pattern for glimpse URLs:
const host = c.req.header("host");
const glimpseUrl = `https://${host}/glimpse/${pageId}`;
Return structured errors with appropriate HTTP status codes:
// 400 for client errors
return c.json({ success: false, error: "Description" }, 400);
// 500 for server errors
return c.json({ success: false, error: "Description", details: result.error }, 500);
Include comprehensive logging:
console.log("Processing URL update webhook request");
console.log("Notion webhook received:", body);
console.log(`Constructed glimpse URL: ${glimpseUrl} for page ID: ${pageId}`);
console.log("Successfully updated Notion page with glimpse URL:", { pageId, url });
console.error("Failed to update Notion page:", result.error);
Return detailed success responses:
return c.json({
success: true,
message: "Page URL updated successfully",
pageId: pageId,
url: glimpseUrl,
timestamp: new Date().toISOString()
});
- Test without header (expect 401)
- Test with wrong key (expect 403)
- Test with correct key (expect 200 or business logic response)
- Test with valid payload structure
- Test with missing page ID (expect 400)
- Test with missing host header (expect 400)
- Test with Notion API failures (expect 500)
Create a test endpoint that simulates the logic without calling external APIs:
app.post("/test-url", async (c) => {
// Same logic but return test response instead of calling Notion API
});
- Don't mix patterns: /tasks/_ routes should use webhook handler pattern, not data API pattern
- Don't forget authentication: All /tasks/_ routes are automatically protected
- Don't skip logging: Webhook endpoints need comprehensive logging for debugging
- Don't forget documentation: Update all relevant README files
- Don't hardcode hosts: Always extract host from request headers
- Don't forget error cases: Handle missing data, API failures, etc.
- Don't skip the catch-all update: Add new endpoints to available endpoints list
Before considering implementation complete:
- Controller follows webhook handler pattern (takes Context, returns HTTP responses)
- Route uses direct controller call pattern
- Service functions return consistent {success, data/error, timestamp} format
- Comprehensive logging for success and failure cases
- Proper HTTP status codes (400 for client errors, 500 for server errors)
- Support for multiple webhook payload structures
- Host extraction from request headers
- Updated all relevant documentation
- Added endpoint to catch-all handler's available endpoints list
- Tested authentication (should fail with test keys)
- Error handling for all edge cases
- The webhook authentication middleware handles all security automatically
- Focus on business logic and proper HTTP handling in the controller
- Keep service functions pure (no business logic, just API calls)
- Document the architectural decision when mixing patterns
- Test thoroughly but remember authentication will block test requests without proper keys
This pattern ensures consistency with the established architecture while providing clear separation of concerns and comprehensive error handling.
Townie, stop here! Before proceeding to additional steps, confirm that this step is working correctly.
If everything is working as intended: conclude this step with these messages:
- Briefly summarize the work that was just completed and why it's important
- Provide the webhook URL
- Tell the user to go to the Glimpse Demos database in Notion and follow these steps:
- Go to the
Glimpse Demos
database - Click the
Webhook
column header and then selectEdit automation
- Under
New action
, selectSend webhook
- Copy the webhook URL and paste it into the
URL
field - Click the
custom header
link under the URL field - For the key add
X-API-KEY
and for the value add theNOTION_WEBHOOK_SECRET
environment variable fromvars.env
file - Save the automation
- Click the
Save URL
button - Click the URL in the
URL
field
- Go to the
Tell the user that the next few steps will add content and HTML to style the page at that URL
.
Add related pages to the /glimpse/:id endpoint by following the instructions in
/_townie/08-related.md