- Add WikiChangeListener service for PostgreSQL notifications - Add webhooks router for HTTP webhook fallback - Add asyncpg dependency for PostgreSQL async support - Include setup scripts and documentation for triggers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.5 KiB
Wiki.js Webhook Setup Guide
Complete guide to enable automatic processing of user-edited wiki pages.
Overview
When you manually edit pages in Wiki.js, the webhook system ensures they're processed identically to AI-generated content:
- ✅ Vectors updated - Qdrant embeddings refreshed
- ✅ Graph updated - Entities extracted, relationships created
- ✅ Bidirectional links - Automatic entity linking both ways
- ✅ Deletions handled - Clean removal from vectors + graph
- ✅ Moves processed - Path updates propagated to graph
Step 1: Configure Wiki.js Webhook
Access Wiki.js Administration
- Log into Wiki.js as administrator
- Navigate to Administration → Webhooks
- Click Add New Webhook
Webhook Configuration
| Field | Value |
|---|---|
| Name | Library Desk Integration |
| Endpoint URL | http://library-desk:8089/webhooks/wikijs |
| Content Type | application/json |
| Events | ☑ page.create ☑ page.update ☑ page.delete ☑ page.rename |
| Status | ✅ Active |
Authentication Header
Add custom header for API authentication:
Authorization: Bearer af88ed8f44bed81bdb20d0534f1c4547340b29e2aba4963f61a71b993d7eb6e5
(Use your actual Library Desk API key from .env)
Step 2: Verify Webhook Endpoint
Test the webhook health endpoint:
curl http://192.168.86.149:8089/webhooks/health
Expected response:
{
"status": "ok",
"service": "webhooks"
}
Step 3: Test Webhook Integration
Test 1: Page Creation
- Create a new page in Wiki.js
- Add content mentioning existing entities (e.g., "Docker", "Kubernetes")
- Save the page
Expected Result:
- Vectors created in Qdrant
- Document node created in Neo4j
- Entities extracted and linked
- Bidirectional links added (new page links to entities, entities' pages link back)
Test 2: Page Update
- Edit an existing page
- Add new content with entity mentions
- Save changes
Expected Result:
- Vectors updated in Qdrant (old removed, new added)
- Graph updated with new entities
- Entity links refreshed
Test 3: Page Deletion
- Delete a page from Wiki.js
Expected Result:
- Vectors removed from Qdrant
- Document node removed from Neo4j
- Orphaned entities cleaned up (entities only mentioned in this page)
- Broken relationships removed
Test 4: Page Move/Rename
- Move a page to different location (e.g.,
/tech/docker→/infrastructure/docker) - Or rename the page
Expected Result:
- Document node path updated in Neo4j
- Entity links updated if title changed
- Vectors remain valid (no re-embedding needed unless content changed)
Event Processing Details
page.create
User creates page → Webhook → Library Desk
├─ Ingest page (vector + graph)
├─ Extract entities
├─ Forward linking (page → entities)
└─ Backward linking (entities → page)
page.update
User edits page → Webhook → Library Desk
├─ Re-ingest page (force refresh)
├─ Update entities
├─ Refresh forward links
└─ Refresh backward links
page.delete
User deletes page → Webhook → Library Desk
├─ Remove vectors from Qdrant
├─ Find orphaned entities
├─ Delete Document node
├─ Delete orphaned entities
└─ Clean broken relationships
page.rename
User moves/renames page → Webhook → Library Desk
├─ Update Document node path
├─ Update title if changed
├─ Re-link entities if title changed
└─ Update references
Monitoring Webhook Activity
Check Logs
# Library Desk logs
docker logs library-desk --follow | grep webhook
# Look for:
# - "Received Wiki.js webhook: page.create"
# - "Processing page.update for page 123"
# - "Cleanup complete for deleted page 456"
Verify Processing
After making changes in Wiki.js, verify:
1. Vectors Updated:
curl -X POST http://192.168.86.149:8089/query/hybrid \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "your page content", "user": "jpmschweitzer"}'
2. Graph Updated:
// In Neo4j Browser
MATCH (d:Document {page_id: 123})
OPTIONAL MATCH (d)-[:MENTIONS]->(e)
RETURN d, collect(e.name) as entities
3. Entity Links Added: Visit the page in Wiki.js and verify entity names are hyperlinked.
Troubleshooting
Webhook Not Triggering
Problem: Changes in Wiki.js don't trigger processing
Solutions:
- Check webhook configuration in Wiki.js Admin
- Verify endpoint URL is correct:
http://library-desk:8089/webhooks/wikijs - Check authorization header is set
- Verify library-desk is running:
docker ps | grep library-desk - Check library-desk logs:
docker logs library-desk --tail 50
Processing Failures
Problem: Webhook triggers but processing fails
Solutions:
- Check library-desk logs for errors
- Verify Neo4j is accessible
- Verify Qdrant is accessible
- Check Ollama is running (for embeddings)
- Verify user mapping in
extract_user_from_email()
Orphaned Entities
Problem: Deleted pages leave entities behind
Solution: Manually clean orphaned entities:
// Find orphaned entities (no MENTIONS relationships)
MATCH (e:User_Jpmschweitzer)
WHERE NOT e:Document
AND NOT EXISTS {(d:Document)-[:MENTIONS]->(e)}
DETACH DELETE e
RETURN count(e) as deleted
Broken Links
Problem: Entity links broken after page moves
Solution: Re-run entity linking on all pages:
curl -X POST http://192.168.86.149:8089/entity-linking/link-all \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"user": "jpmschweitzer"}'
Performance Considerations
Background Processing
Webhook handler processes events in the background to avoid blocking Wiki.js:
- Webhook response: <100ms (immediate acknowledgment)
- Processing: 2-10s (runs asynchronously)
- No impact on Wiki.js editing experience
Rate Limiting
For bulk operations (importing many pages):
- Consider temporarily disabling webhook
- Use batch ingestion endpoint instead:
/ingest/batch - Re-enable webhook after bulk import
Advanced Configuration
Custom User Mapping
By default, user is extracted from email (user@domain → user).
Customize in /services/library-desk/src/routers/webhooks.py:
def extract_user_from_email(email: str) -> str:
# Option 1: Map specific emails to users
user_map = {
"admin@example.com": "admin",
"john.doe@example.com": "jpmschweitzer"
}
return user_map.get(email, email.split("@")[0])
Event Filtering
To process only certain pages, add filtering in webhook handler:
# Only process pages in /tech/ path
if not payload.page.get("path", "").startswith("/tech/"):
logger.info(f"Skipping page outside /tech/: {payload.page.get('path')}")
return
Disable Bidirectional Linking
To only do forward linking (page → entities) without backward linking:
Edit process_wiki_page_change():
# Comment out the bidirectional linking call
# link_stats = await consolidation_service._apply_bidirectional_entity_linking(...)
Summary
With webhooks configured:
| User Action | Automatic Processing |
|---|---|
| Create page | Ingest + entity link (bidirectional) |
| Edit page | Re-ingest + refresh links |
| Delete page | Clean vectors + graph + orphaned entities |
| Move page | Update paths + refresh links |
Result: User-edited and AI-generated pages are indistinguishable in the knowledge base!