{"title":"Webhook Automation Guide","slug":"webhook-automation","category":"guides","summary":"","audience":["human"],"tags":[],"word_count":393,"read_minutes":2,"llm_context":"Practical patterns for using Synapse webhooks to automate workflows. Covers logging, summarization, external tool integration, sync to note-taking apps, and monitoring. Events: memory.created, memory.updated, memory.deleted, memory.verified, memory.unverified, task.updated, chat.message_sent.","lang":"en","translated":true,"requested_lang":"en","content_markdown":"# Webhook Automation Guide\n\nPractical patterns for automating workflows with Synapse webhooks.\n\n## Pattern 1: Memory Log\n\nLog every new memory to a file or external service.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    # Verify signature\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'memory.created':\n        data = request.get_json()\n        with open('memory-log.txt', 'a') as f:\n            f.write(f\"{data['event']}: {data['data']['key']} = {data['data']['content'][:100]}\\n\")\n        return jsonify({'status': 'ok'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThe 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.\n\n## Pattern 2: Memory Summary\n\nSummarize new memories using an LLM and store the summary.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event in ('memory.created', 'memory.updated'):\n        data = request.get_json()\n        content = data['data']['content']\n        # Send to your LLM for summarization\n        summary = summarize_with_llm(content)  # your function\n        store_summary(data['data']['key'], summary)\n        return jsonify({'status': 'summarized'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 3: Sync to Note-Taking App\n\nForward new memories to Notion, Obsidian, or similar tools.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac, requests\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\nNOTION_API_KEY = 'your-notion-key'\nNOTION_DATABASE_ID = 'your-database-id'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'memory.created':\n        data = request.get_json()\n        # Create Notion page\n        requests.post('https://api.notion.com/v1/pages', headers={\n            'Authorization': f'Bearer {NOTION_API_KEY}',\n            'Content-Type': 'application/json',\n            'Notion-Version': '2022-06-28',\n        }, json={\n            'parent': {'database_id': NOTION_DATABASE_ID},\n            'properties': {\n                'Title': {'title': [{'text': {'content': data['data']['key']}}]},\n                'Content': {'rich_text': [{'text': {'content': data['data']['content'][:2000]}}]},\n                'Category': {'select': {'name': data['data'].get('category', 'note')}},\n            },\n        })\n        return jsonify({'status': 'synced'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 4: Monitoring & Alerting\n\nGet notified when memories are deleted or when tasks fail.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    data = request.get_json()\n\n    if event == 'memory.deleted':\n        send_alert(f\"Memory deleted: {data['data']['key']}\")\n    elif event == 'task.updated':\n        task_data = data['data']\n        if task_data.get('status') == 'failed':\n            send_alert(f\"Task failed: {task_data.get('name', 'unknown')}\")\n\n    return jsonify({'status': 'ok'}), 200\n\ndef send_alert(message):\n    # Send to Slack, Discord, email, etc.\n    print(f'ALERT: {message}')\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 5: Chat Message Relay\n\nForward chat messages to an external system for logging or analysis.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'chat.message_sent':\n        data = request.get_json()\n        # Process chat message\n        log_chat_message(data['data'])\n        return jsonify({'status': 'ok'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Conditional Trigger Patterns (v1.18.14)\n\nRegister a webhook that fires **only** when the event payload satisfies a\ncategory/tag/priority combination — e.g. alert only on critical security facts:\n\n```bash\ncurl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H \"Authorization: Bearer $MIND_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://example.com/hook\",\n    \"events\": \"memory.created,memory.updated\",\n    \"secret\": \"signing-secret-32chars\",\n    \"filter\": {\n      \"categories\": [\"fact\", \"project\"],\n      \"tags\": [\"sec\", \"crit\"],\n      \"tag_mode\": \"any\",\n      \"priority_min\": \"high\"\n    }\n  }'\n```\n\nFilter fields (all optional, but at least one required):\n\n| Field | Meaning |\n|---|---|\n| `categories` | fire only if `data.category` is one of these |\n| `tags` | fire only if `data.tags` contains the given tag(s) |\n| `tag_mode` | `any` (default) = at least one tag matches; `all` = every listed tag must be present |\n| `priority_min` | fire only if payload priority rank ≥ this (`low < normal < high < critical`) |\n\nSafe defaults: events whose payload has no category/tags (e.g. `memory.deleted`,\nchat events) never fire filtered webhooks; a corrupt `filter` JSON is treated as\nunfiltered (the webhook stays active); `/webhooks/:id/test` ignores the filter\non purpose. Note: only `memory.created` / `memory.updated` payloads carry\n`category`/`tags`/`priority` — including the GET alternatives\n(`GET /memory/store`, `GET /memory/:id/update`), which fire webhooks since\nv1.18.14.\n","content_html":"<h1>Webhook Automation Guide</h1>\n<p>Practical patterns for automating workflows with Synapse webhooks.</p>\n<h2>Pattern 1: Memory Log</h2>\n<p>Log every new memory to a file or external service.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    <span class=\"hljs-comment\"># Verify signature</span>\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.created&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-keyword\">with</span> <span class=\"hljs-built_in\">open</span>(<span class=\"hljs-string\">&#x27;memory-log.txt&#x27;</span>, <span class=\"hljs-string\">&#x27;a&#x27;</span>) <span class=\"hljs-keyword\">as</span> f:\n            f.write(<span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;event&#x27;</span>]}</span>: <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}</span> = <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>][:<span class=\"hljs-number\">100</span>]}</span>\\n&quot;</span>)\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>The handler listens for <code>memory.created</code> events and logs each new memory&#39;s key and a content preview to a file. This is useful for auditing, debugging, or creating a secondary backup of important memories.</p>\n<h2>Pattern 2: Memory Summary</h2>\n<p>Summarize new memories using an LLM and store the summary.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event <span class=\"hljs-keyword\">in</span> (<span class=\"hljs-string\">&#x27;memory.created&#x27;</span>, <span class=\"hljs-string\">&#x27;memory.updated&#x27;</span>):\n        data = request.get_json()\n        content = data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>]\n        <span class=\"hljs-comment\"># Send to your LLM for summarization</span>\n        summary = summarize_with_llm(content)  <span class=\"hljs-comment\"># your function</span>\n        store_summary(data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>], summary)\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;summarized&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>This pattern captures both <code>memory.created</code> and <code>memory.updated</code> events, sends the content to an LLM for summarization, and stores the result. Useful for maintaining a condensed knowledge base.</p>\n<h2>Pattern 3: Sync to Note-Taking App</h2>\n<p>Forward new memories to Notion, Obsidian, or similar tools.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac, requests\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\nNOTION_API_KEY = <span class=\"hljs-string\">&#x27;your-notion-key&#x27;</span>\nNOTION_DATABASE_ID = <span class=\"hljs-string\">&#x27;your-database-id&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.created&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-comment\"># Create Notion page</span>\n        requests.post(<span class=\"hljs-string\">&#x27;https://api.notion.com/v1/pages&#x27;</span>, headers={\n            <span class=\"hljs-string\">&#x27;Authorization&#x27;</span>: <span class=\"hljs-string\">f&#x27;Bearer <span class=\"hljs-subst\">{NOTION_API_KEY}</span>&#x27;</span>,\n            <span class=\"hljs-string\">&#x27;Content-Type&#x27;</span>: <span class=\"hljs-string\">&#x27;application/json&#x27;</span>,\n            <span class=\"hljs-string\">&#x27;Notion-Version&#x27;</span>: <span class=\"hljs-string\">&#x27;2022-06-28&#x27;</span>,\n        }, json={\n            <span class=\"hljs-string\">&#x27;parent&#x27;</span>: {<span class=\"hljs-string\">&#x27;database_id&#x27;</span>: NOTION_DATABASE_ID},\n            <span class=\"hljs-string\">&#x27;properties&#x27;</span>: {\n                <span class=\"hljs-string\">&#x27;Title&#x27;</span>: {<span class=\"hljs-string\">&#x27;title&#x27;</span>: [{<span class=\"hljs-string\">&#x27;text&#x27;</span>: {<span class=\"hljs-string\">&#x27;content&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}}]},\n                <span class=\"hljs-string\">&#x27;Content&#x27;</span>: {<span class=\"hljs-string\">&#x27;rich_text&#x27;</span>: [{<span class=\"hljs-string\">&#x27;text&#x27;</span>: {<span class=\"hljs-string\">&#x27;content&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>][:<span class=\"hljs-number\">2000</span>]}}]},\n                <span class=\"hljs-string\">&#x27;Category&#x27;</span>: {<span class=\"hljs-string\">&#x27;select&#x27;</span>: {<span class=\"hljs-string\">&#x27;name&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>].get(<span class=\"hljs-string\">&#x27;category&#x27;</span>, <span class=\"hljs-string\">&#x27;note&#x27;</span>)}},\n            },\n        })\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;synced&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>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.</p>\n<h2>Pattern 4: Monitoring &amp; Alerting</h2>\n<p>Get notified when memories are deleted or when tasks fail.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    data = request.get_json()\n\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.deleted&#x27;</span>:\n        send_alert(<span class=\"hljs-string\">f&quot;Memory deleted: <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}</span>&quot;</span>)\n    <span class=\"hljs-keyword\">elif</span> event == <span class=\"hljs-string\">&#x27;task.updated&#x27;</span>:\n        task_data = data[<span class=\"hljs-string\">&#x27;data&#x27;</span>]\n        <span class=\"hljs-keyword\">if</span> task_data.get(<span class=\"hljs-string\">&#x27;status&#x27;</span>) == <span class=\"hljs-string\">&#x27;failed&#x27;</span>:\n            send_alert(<span class=\"hljs-string\">f&quot;Task failed: <span class=\"hljs-subst\">{task_data.get(<span class=\"hljs-string\">&#x27;name&#x27;</span>, <span class=\"hljs-string\">&#x27;unknown&#x27;</span>)}</span>&quot;</span>)\n\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">send_alert</span>(<span class=\"hljs-params\">message</span>):\n    <span class=\"hljs-comment\"># Send to Slack, Discord, email, etc.</span>\n    <span class=\"hljs-built_in\">print</span>(<span class=\"hljs-string\">f&#x27;ALERT: <span class=\"hljs-subst\">{message}</span>&#x27;</span>)</code></pre><h3>Explanation</h3>\n<p>This pattern monitors for <code>memory.deleted</code> and <code>task.updated</code> events. When a memory is deleted or a task fails, it triggers an alert. Useful for monitoring and ensuring data integrity.</p>\n<h2>Pattern 5: Chat Message Relay</h2>\n<p>Forward chat messages to an external system for logging or analysis.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;chat.message_sent&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-comment\"># Process chat message</span>\n        log_chat_message(data[<span class=\"hljs-string\">&#x27;data&#x27;</span>])\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>This pattern listens for <code>chat.message_sent</code> events and forwards them to an external logging or analysis system. Useful for conversation analytics, compliance logging, or integration with CRM systems.</p>\n<h2>Conditional Trigger Patterns (v1.18.14)</h2>\n<p>Register a webhook that fires <strong>only</strong> when the event payload satisfies a\ncategory/tag/priority combination — e.g. alert only on critical security facts:</p>\n<pre><code class=\"hljs language-bash\">curl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$MIND_KEY</span>&quot;</span> \\\n  -H <span class=\"hljs-string\">&quot;Content-Type: application/json&quot;</span> \\\n  -d <span class=\"hljs-string\">&#x27;{\n    &quot;url&quot;: &quot;https://example.com/hook&quot;,\n    &quot;events&quot;: &quot;memory.created,memory.updated&quot;,\n    &quot;secret&quot;: &quot;signing-secret-32chars&quot;,\n    &quot;filter&quot;: {\n      &quot;categories&quot;: [&quot;fact&quot;, &quot;project&quot;],\n      &quot;tags&quot;: [&quot;sec&quot;, &quot;crit&quot;],\n      &quot;tag_mode&quot;: &quot;any&quot;,\n      &quot;priority_min&quot;: &quot;high&quot;\n    }\n  }&#x27;</span></code></pre><p>Filter fields (all optional, but at least one required):</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>categories</code></td>\n<td>fire only if <code>data.category</code> is one of these</td>\n</tr>\n<tr>\n<td><code>tags</code></td>\n<td>fire only if <code>data.tags</code> contains the given tag(s)</td>\n</tr>\n<tr>\n<td><code>tag_mode</code></td>\n<td><code>any</code> (default) = at least one tag matches; <code>all</code> = every listed tag must be present</td>\n</tr>\n<tr>\n<td><code>priority_min</code></td>\n<td>fire only if payload priority rank ≥ this (<code>low &lt; normal &lt; high &lt; critical</code>)</td>\n</tr>\n</tbody></table>\n<p>Safe defaults: events whose payload has no category/tags (e.g. <code>memory.deleted</code>,\nchat events) never fire filtered webhooks; a corrupt <code>filter</code> JSON is treated as\nunfiltered (the webhook stays active); <code>/webhooks/:id/test</code> ignores the filter\non purpose. Note: only <code>memory.created</code> / <code>memory.updated</code> payloads carry\n<code>category</code>/<code>tags</code>/<code>priority</code> — including the GET alternatives\n(<code>GET /memory/store</code>, <code>GET /memory/:id/update</code>), which fire webhooks since\nv1.18.14.</p>\n","urls":{"html":"/docs/guides/webhook-automation","text":"/docs/guides/webhook-automation?format=text","json":"/docs/guides/webhook-automation?format=json","llm":"/docs/guides/webhook-automation?format=llm"},"translations_available":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"],"wordCount":393,"readMinutes":2,"llmContext":"Practical patterns for using Synapse webhooks to automate workflows. Covers logging, summarization, external tool integration, sync to note-taking apps, and monitoring. Events: memory.created, memory.updated, memory.deleted, memory.verified, memory.unverified, task.updated, chat.message_sent.","requestedLang":"en","contentMarkdown":"# Webhook Automation Guide\n\nPractical patterns for automating workflows with Synapse webhooks.\n\n## Pattern 1: Memory Log\n\nLog every new memory to a file or external service.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    # Verify signature\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'memory.created':\n        data = request.get_json()\n        with open('memory-log.txt', 'a') as f:\n            f.write(f\"{data['event']}: {data['data']['key']} = {data['data']['content'][:100]}\\n\")\n        return jsonify({'status': 'ok'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThe 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.\n\n## Pattern 2: Memory Summary\n\nSummarize new memories using an LLM and store the summary.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event in ('memory.created', 'memory.updated'):\n        data = request.get_json()\n        content = data['data']['content']\n        # Send to your LLM for summarization\n        summary = summarize_with_llm(content)  # your function\n        store_summary(data['data']['key'], summary)\n        return jsonify({'status': 'summarized'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 3: Sync to Note-Taking App\n\nForward new memories to Notion, Obsidian, or similar tools.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac, requests\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\nNOTION_API_KEY = 'your-notion-key'\nNOTION_DATABASE_ID = 'your-database-id'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'memory.created':\n        data = request.get_json()\n        # Create Notion page\n        requests.post('https://api.notion.com/v1/pages', headers={\n            'Authorization': f'Bearer {NOTION_API_KEY}',\n            'Content-Type': 'application/json',\n            'Notion-Version': '2022-06-28',\n        }, json={\n            'parent': {'database_id': NOTION_DATABASE_ID},\n            'properties': {\n                'Title': {'title': [{'text': {'content': data['data']['key']}}]},\n                'Content': {'rich_text': [{'text': {'content': data['data']['content'][:2000]}}]},\n                'Category': {'select': {'name': data['data'].get('category', 'note')}},\n            },\n        })\n        return jsonify({'status': 'synced'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 4: Monitoring & Alerting\n\nGet notified when memories are deleted or when tasks fail.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    data = request.get_json()\n\n    if event == 'memory.deleted':\n        send_alert(f\"Memory deleted: {data['data']['key']}\")\n    elif event == 'task.updated':\n        task_data = data['data']\n        if task_data.get('status') == 'failed':\n            send_alert(f\"Task failed: {task_data.get('name', 'unknown')}\")\n\n    return jsonify({'status': 'ok'}), 200\n\ndef send_alert(message):\n    # Send to Slack, Discord, email, etc.\n    print(f'ALERT: {message}')\n```\n\n### Explanation\n\nThis 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.\n\n## Pattern 5: Chat Message Relay\n\nForward chat messages to an external system for logging or analysis.\n\n### Setup\n\n```python\nfrom flask import Flask, request, jsonify\nimport hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = 'your-webhook-secret'\n\n@app.route('/webhook', methods=['POST'])\ndef webhook():\n    sig = request.headers.get('X-Synapse-Signature', '')\n    expected = 'sha256=' + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(sig, expected):\n        return jsonify({'error': 'invalid signature'}), 401\n\n    event = request.headers.get('X-Synapse-Event')\n    if event == 'chat.message_sent':\n        data = request.get_json()\n        # Process chat message\n        log_chat_message(data['data'])\n        return jsonify({'status': 'ok'}), 200\n    return jsonify({'status': 'ignored'}), 200\n```\n\n### Explanation\n\nThis 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.\n\n## Conditional Trigger Patterns (v1.18.14)\n\nRegister a webhook that fires **only** when the event payload satisfies a\ncategory/tag/priority combination — e.g. alert only on critical security facts:\n\n```bash\ncurl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H \"Authorization: Bearer $MIND_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://example.com/hook\",\n    \"events\": \"memory.created,memory.updated\",\n    \"secret\": \"signing-secret-32chars\",\n    \"filter\": {\n      \"categories\": [\"fact\", \"project\"],\n      \"tags\": [\"sec\", \"crit\"],\n      \"tag_mode\": \"any\",\n      \"priority_min\": \"high\"\n    }\n  }'\n```\n\nFilter fields (all optional, but at least one required):\n\n| Field | Meaning |\n|---|---|\n| `categories` | fire only if `data.category` is one of these |\n| `tags` | fire only if `data.tags` contains the given tag(s) |\n| `tag_mode` | `any` (default) = at least one tag matches; `all` = every listed tag must be present |\n| `priority_min` | fire only if payload priority rank ≥ this (`low < normal < high < critical`) |\n\nSafe defaults: events whose payload has no category/tags (e.g. `memory.deleted`,\nchat events) never fire filtered webhooks; a corrupt `filter` JSON is treated as\nunfiltered (the webhook stays active); `/webhooks/:id/test` ignores the filter\non purpose. Note: only `memory.created` / `memory.updated` payloads carry\n`category`/`tags`/`priority` — including the GET alternatives\n(`GET /memory/store`, `GET /memory/:id/update`), which fire webhooks since\nv1.18.14.\n","contentHtml":"<h1>Webhook Automation Guide</h1>\n<p>Practical patterns for automating workflows with Synapse webhooks.</p>\n<h2>Pattern 1: Memory Log</h2>\n<p>Log every new memory to a file or external service.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    <span class=\"hljs-comment\"># Verify signature</span>\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.created&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-keyword\">with</span> <span class=\"hljs-built_in\">open</span>(<span class=\"hljs-string\">&#x27;memory-log.txt&#x27;</span>, <span class=\"hljs-string\">&#x27;a&#x27;</span>) <span class=\"hljs-keyword\">as</span> f:\n            f.write(<span class=\"hljs-string\">f&quot;<span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;event&#x27;</span>]}</span>: <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}</span> = <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>][:<span class=\"hljs-number\">100</span>]}</span>\\n&quot;</span>)\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>The handler listens for <code>memory.created</code> events and logs each new memory&#39;s key and a content preview to a file. This is useful for auditing, debugging, or creating a secondary backup of important memories.</p>\n<h2>Pattern 2: Memory Summary</h2>\n<p>Summarize new memories using an LLM and store the summary.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event <span class=\"hljs-keyword\">in</span> (<span class=\"hljs-string\">&#x27;memory.created&#x27;</span>, <span class=\"hljs-string\">&#x27;memory.updated&#x27;</span>):\n        data = request.get_json()\n        content = data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>]\n        <span class=\"hljs-comment\"># Send to your LLM for summarization</span>\n        summary = summarize_with_llm(content)  <span class=\"hljs-comment\"># your function</span>\n        store_summary(data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>], summary)\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;summarized&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>This pattern captures both <code>memory.created</code> and <code>memory.updated</code> events, sends the content to an LLM for summarization, and stores the result. Useful for maintaining a condensed knowledge base.</p>\n<h2>Pattern 3: Sync to Note-Taking App</h2>\n<p>Forward new memories to Notion, Obsidian, or similar tools.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac, requests\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\nNOTION_API_KEY = <span class=\"hljs-string\">&#x27;your-notion-key&#x27;</span>\nNOTION_DATABASE_ID = <span class=\"hljs-string\">&#x27;your-database-id&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.created&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-comment\"># Create Notion page</span>\n        requests.post(<span class=\"hljs-string\">&#x27;https://api.notion.com/v1/pages&#x27;</span>, headers={\n            <span class=\"hljs-string\">&#x27;Authorization&#x27;</span>: <span class=\"hljs-string\">f&#x27;Bearer <span class=\"hljs-subst\">{NOTION_API_KEY}</span>&#x27;</span>,\n            <span class=\"hljs-string\">&#x27;Content-Type&#x27;</span>: <span class=\"hljs-string\">&#x27;application/json&#x27;</span>,\n            <span class=\"hljs-string\">&#x27;Notion-Version&#x27;</span>: <span class=\"hljs-string\">&#x27;2022-06-28&#x27;</span>,\n        }, json={\n            <span class=\"hljs-string\">&#x27;parent&#x27;</span>: {<span class=\"hljs-string\">&#x27;database_id&#x27;</span>: NOTION_DATABASE_ID},\n            <span class=\"hljs-string\">&#x27;properties&#x27;</span>: {\n                <span class=\"hljs-string\">&#x27;Title&#x27;</span>: {<span class=\"hljs-string\">&#x27;title&#x27;</span>: [{<span class=\"hljs-string\">&#x27;text&#x27;</span>: {<span class=\"hljs-string\">&#x27;content&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}}]},\n                <span class=\"hljs-string\">&#x27;Content&#x27;</span>: {<span class=\"hljs-string\">&#x27;rich_text&#x27;</span>: [{<span class=\"hljs-string\">&#x27;text&#x27;</span>: {<span class=\"hljs-string\">&#x27;content&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;content&#x27;</span>][:<span class=\"hljs-number\">2000</span>]}}]},\n                <span class=\"hljs-string\">&#x27;Category&#x27;</span>: {<span class=\"hljs-string\">&#x27;select&#x27;</span>: {<span class=\"hljs-string\">&#x27;name&#x27;</span>: data[<span class=\"hljs-string\">&#x27;data&#x27;</span>].get(<span class=\"hljs-string\">&#x27;category&#x27;</span>, <span class=\"hljs-string\">&#x27;note&#x27;</span>)}},\n            },\n        })\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;synced&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>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.</p>\n<h2>Pattern 4: Monitoring &amp; Alerting</h2>\n<p>Get notified when memories are deleted or when tasks fail.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    data = request.get_json()\n\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;memory.deleted&#x27;</span>:\n        send_alert(<span class=\"hljs-string\">f&quot;Memory deleted: <span class=\"hljs-subst\">{data[<span class=\"hljs-string\">&#x27;data&#x27;</span>][<span class=\"hljs-string\">&#x27;key&#x27;</span>]}</span>&quot;</span>)\n    <span class=\"hljs-keyword\">elif</span> event == <span class=\"hljs-string\">&#x27;task.updated&#x27;</span>:\n        task_data = data[<span class=\"hljs-string\">&#x27;data&#x27;</span>]\n        <span class=\"hljs-keyword\">if</span> task_data.get(<span class=\"hljs-string\">&#x27;status&#x27;</span>) == <span class=\"hljs-string\">&#x27;failed&#x27;</span>:\n            send_alert(<span class=\"hljs-string\">f&quot;Task failed: <span class=\"hljs-subst\">{task_data.get(<span class=\"hljs-string\">&#x27;name&#x27;</span>, <span class=\"hljs-string\">&#x27;unknown&#x27;</span>)}</span>&quot;</span>)\n\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">send_alert</span>(<span class=\"hljs-params\">message</span>):\n    <span class=\"hljs-comment\"># Send to Slack, Discord, email, etc.</span>\n    <span class=\"hljs-built_in\">print</span>(<span class=\"hljs-string\">f&#x27;ALERT: <span class=\"hljs-subst\">{message}</span>&#x27;</span>)</code></pre><h3>Explanation</h3>\n<p>This pattern monitors for <code>memory.deleted</code> and <code>task.updated</code> events. When a memory is deleted or a task fails, it triggers an alert. Useful for monitoring and ensuring data integrity.</p>\n<h2>Pattern 5: Chat Message Relay</h2>\n<p>Forward chat messages to an external system for logging or analysis.</p>\n<h3>Setup</h3>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">from</span> flask <span class=\"hljs-keyword\">import</span> Flask, request, jsonify\n<span class=\"hljs-keyword\">import</span> hashlib, hmac\n\napp = Flask(__name__)\nWEBHOOK_SECRET = <span class=\"hljs-string\">&#x27;your-webhook-secret&#x27;</span>\n\n<span class=\"hljs-meta\">@app.route(<span class=\"hljs-params\"><span class=\"hljs-string\">&#x27;/webhook&#x27;</span>, methods=[<span class=\"hljs-string\">&#x27;POST&#x27;</span>]</span>)</span>\n<span class=\"hljs-keyword\">def</span> <span class=\"hljs-title function_\">webhook</span>():\n    sig = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n    expected = <span class=\"hljs-string\">&#x27;sha256=&#x27;</span> + hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()\n    <span class=\"hljs-keyword\">if</span> <span class=\"hljs-keyword\">not</span> hmac.compare_digest(sig, expected):\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;error&#x27;</span>: <span class=\"hljs-string\">&#x27;invalid signature&#x27;</span>}), <span class=\"hljs-number\">401</span>\n\n    event = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Event&#x27;</span>)\n    <span class=\"hljs-keyword\">if</span> event == <span class=\"hljs-string\">&#x27;chat.message_sent&#x27;</span>:\n        data = request.get_json()\n        <span class=\"hljs-comment\"># Process chat message</span>\n        log_chat_message(data[<span class=\"hljs-string\">&#x27;data&#x27;</span>])\n        <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ok&#x27;</span>}), <span class=\"hljs-number\">200</span>\n    <span class=\"hljs-keyword\">return</span> jsonify({<span class=\"hljs-string\">&#x27;status&#x27;</span>: <span class=\"hljs-string\">&#x27;ignored&#x27;</span>}), <span class=\"hljs-number\">200</span></code></pre><h3>Explanation</h3>\n<p>This pattern listens for <code>chat.message_sent</code> events and forwards them to an external logging or analysis system. Useful for conversation analytics, compliance logging, or integration with CRM systems.</p>\n<h2>Conditional Trigger Patterns (v1.18.14)</h2>\n<p>Register a webhook that fires <strong>only</strong> when the event payload satisfies a\ncategory/tag/priority combination — e.g. alert only on critical security facts:</p>\n<pre><code class=\"hljs language-bash\">curl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$MIND_KEY</span>&quot;</span> \\\n  -H <span class=\"hljs-string\">&quot;Content-Type: application/json&quot;</span> \\\n  -d <span class=\"hljs-string\">&#x27;{\n    &quot;url&quot;: &quot;https://example.com/hook&quot;,\n    &quot;events&quot;: &quot;memory.created,memory.updated&quot;,\n    &quot;secret&quot;: &quot;signing-secret-32chars&quot;,\n    &quot;filter&quot;: {\n      &quot;categories&quot;: [&quot;fact&quot;, &quot;project&quot;],\n      &quot;tags&quot;: [&quot;sec&quot;, &quot;crit&quot;],\n      &quot;tag_mode&quot;: &quot;any&quot;,\n      &quot;priority_min&quot;: &quot;high&quot;\n    }\n  }&#x27;</span></code></pre><p>Filter fields (all optional, but at least one required):</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>categories</code></td>\n<td>fire only if <code>data.category</code> is one of these</td>\n</tr>\n<tr>\n<td><code>tags</code></td>\n<td>fire only if <code>data.tags</code> contains the given tag(s)</td>\n</tr>\n<tr>\n<td><code>tag_mode</code></td>\n<td><code>any</code> (default) = at least one tag matches; <code>all</code> = every listed tag must be present</td>\n</tr>\n<tr>\n<td><code>priority_min</code></td>\n<td>fire only if payload priority rank ≥ this (<code>low &lt; normal &lt; high &lt; critical</code>)</td>\n</tr>\n</tbody></table>\n<p>Safe defaults: events whose payload has no category/tags (e.g. <code>memory.deleted</code>,\nchat events) never fire filtered webhooks; a corrupt <code>filter</code> JSON is treated as\nunfiltered (the webhook stays active); <code>/webhooks/:id/test</code> ignores the filter\non purpose. Note: only <code>memory.created</code> / <code>memory.updated</code> payloads carry\n<code>category</code>/<code>tags</code>/<code>priority</code> — including the GET alternatives\n(<code>GET /memory/store</code>, <code>GET /memory/:id/update</code>), which fire webhooks since\nv1.18.14.</p>\n","translationsAvailable":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"]}