Webhook Automation Guide
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
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'}), 200Explanation
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
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'}), 200Explanation
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
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'}), 200Explanation
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
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
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'}), 200Explanation
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:
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.