# Webhook Automation Guide Practical patterns for automating workflows with Synapse webhooks. ## Pattern 1: Memory Log Log every new memory to a file or external service. ### Setup ```python from flask import Flask, request, jsonify import hashlib, hmac app = Flask(__name__) WEBHOOK_SECRET = 'your-webhook-secret' @app.route('/webhook', methods=['POST']) def webhook(): # Verify signature sig = request.headers.get('X-Synapse-Signature', '') expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify({'error': 'invalid signature'}), 401 event = request.headers.get('X-Synapse-Event') if event == 'memory.created': data = request.get_json() with open('memory-log.txt', 'a') as f: f.write(f"{data['event']}: {data['data']['key']} = {data['data']['content'][:100]}\n") return jsonify({'status': 'ok'}), 200 return jsonify({'status': 'ignored'}), 200 ``` ### Explanation The handler listens for `memory.created` events and logs each new memory's key and a content preview to a file. This is useful for auditing, debugging, or creating a secondary backup of important memories. ## Pattern 2: Memory Summary Summarize new memories using an LLM and store the summary. ### Setup ```python from flask import Flask, request, jsonify import hashlib, hmac app = Flask(__name__) WEBHOOK_SECRET = 'your-webhook-secret' @app.route('/webhook', methods=['POST']) def webhook(): sig = request.headers.get('X-Synapse-Signature', '') expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify({'error': 'invalid signature'}), 401 event = request.headers.get('X-Synapse-Event') if event in ('memory.created', 'memory.updated'): data = request.get_json() content = data['data']['content'] # Send to your LLM for summarization summary = summarize_with_llm(content) # your function store_summary(data['data']['key'], summary) return jsonify({'status': 'summarized'}), 200 return jsonify({'status': 'ignored'}), 200 ``` ### Explanation This pattern captures both `memory.created` and `memory.updated` events, sends the content to an LLM for summarization, and stores the result. Useful for maintaining a condensed knowledge base. ## Pattern 3: Sync to Note-Taking App Forward new memories to Notion, Obsidian, or similar tools. ### Setup ```python from flask import Flask, request, jsonify import hashlib, hmac, requests app = Flask(__name__) WEBHOOK_SECRET = 'your-webhook-secret' NOTION_API_KEY = 'your-notion-key' NOTION_DATABASE_ID = 'your-database-id' @app.route('/webhook', methods=['POST']) def webhook(): sig = request.headers.get('X-Synapse-Signature', '') expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify({'error': 'invalid signature'}), 401 event = request.headers.get('X-Synapse-Event') if event == 'memory.created': data = request.get_json() # Create Notion page requests.post('https://api.notion.com/v1/pages', headers={ 'Authorization': f'Bearer {NOTION_API_KEY}', 'Content-Type': 'application/json', 'Notion-Version': '2022-06-28', }, json={ 'parent': {'database_id': NOTION_DATABASE_ID}, 'properties': { 'Title': {'title': [{'text': {'content': data['data']['key']}}]}, 'Content': {'rich_text': [{'text': {'content': data['data']['content'][:2000]}}]}, 'Category': {'select': {'name': data['data'].get('category', 'note')}}, }, }) return jsonify({'status': 'synced'}), 200 return jsonify({'status': 'ignored'}), 200 ``` ### Explanation This pattern forwards each new memory to a Notion database, preserving the key as the title, content as a text property, and category as a select field. Adapt the API call for Obsidian, Logseq, or other tools. ## Pattern 4: Monitoring & Alerting Get notified when memories are deleted or when tasks fail. ### Setup ```python from flask import Flask, request, jsonify import hashlib, hmac app = Flask(__name__) WEBHOOK_SECRET = 'your-webhook-secret' @app.route('/webhook', methods=['POST']) def webhook(): sig = request.headers.get('X-Synapse-Signature', '') expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify({'error': 'invalid signature'}), 401 event = request.headers.get('X-Synapse-Event') data = request.get_json() if event == 'memory.deleted': send_alert(f"Memory deleted: {data['data']['key']}") elif event == 'task.updated': task_data = data['data'] if task_data.get('status') == 'failed': send_alert(f"Task failed: {task_data.get('name', 'unknown')}") return jsonify({'status': 'ok'}), 200 def send_alert(message): # Send to Slack, Discord, email, etc. print(f'ALERT: {message}') ``` ### Explanation This pattern monitors for `memory.deleted` and `task.updated` events. When a memory is deleted or a task fails, it triggers an alert. Useful for monitoring and ensuring data integrity. ## Pattern 5: Chat Message Relay Forward chat messages to an external system for logging or analysis. ### Setup ```python from flask import Flask, request, jsonify import hashlib, hmac app = Flask(__name__) WEBHOOK_SECRET = 'your-webhook-secret' @app.route('/webhook', methods=['POST']) def webhook(): sig = request.headers.get('X-Synapse-Signature', '') expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify({'error': 'invalid signature'}), 401 event = request.headers.get('X-Synapse-Event') if event == 'chat.message_sent': data = request.get_json() # Process chat message log_chat_message(data['data']) return jsonify({'status': 'ok'}), 200 return jsonify({'status': 'ignored'}), 200 ``` ### Explanation This pattern listens for `chat.message_sent` events and forwards them to an external logging or analysis system. Useful for conversation analytics, compliance logging, or integration with CRM systems. ## Conditional Trigger Patterns (v1.18.14) Register a webhook that fires **only** when the event payload satisfies a category/tag/priority combination — e.g. alert only on critical security facts: ```bash curl -X POST https://synapse.schaefer.zone/webhooks \ -H "Authorization: Bearer $MIND_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hook", "events": "memory.created,memory.updated", "secret": "signing-secret-32chars", "filter": { "categories": ["fact", "project"], "tags": ["sec", "crit"], "tag_mode": "any", "priority_min": "high" } }' ``` Filter fields (all optional, but at least one required): | Field | Meaning | |---|---| | `categories` | fire only if `data.category` is one of these | | `tags` | fire only if `data.tags` contains the given tag(s) | | `tag_mode` | `any` (default) = at least one tag matches; `all` = every listed tag must be present | | `priority_min` | fire only if payload priority rank ≥ this (`low < normal < high < critical`) | Safe defaults: events whose payload has no category/tags (e.g. `memory.deleted`, chat events) never fire filtered webhooks; a corrupt `filter` JSON is treated as unfiltered (the webhook stays active); `/webhooks/:id/test` ignores the filter on purpose. Note: only `memory.created` / `memory.updated` payloads carry `category`/`tags`/`priority` — including the GET alternatives (`GET /memory/store`, `GET /memory/:id/update`), which fire webhooks since v1.18.14.