{"title":"Webhooks","slug":"webhooks","category":"api","summary":"","audience":["human"],"tags":[],"word_count":562,"read_minutes":3,"llm_context":"Webhook management via REST API. Create, list, get, delete, enable/disable and test webhooks. Events: memory.created, memory.updated, memory.deleted, memory.verified, memory.unverified, task.updated, chat.message_sent. Webhooks fire HTTP POST to configured URL with HMAC-SHA256 signature.","lang":"en","translated":true,"requested_lang":"en","content_markdown":"# Webhooks\n\n## Overview\n\nWebhooks allow you to receive HTTP callbacks when events occur in your Synapse mind. When an event triggers, Synapse sends a `POST` request to the URL you configured, with a JSON body containing event data and a HMAC-SHA256 signature for verification.\n\nWebhooks have a **20 webhooks per mind** limit.\n\n## Events\n\n| Event | Description |\n|-------|-------------|\n| `memory.created` | Fired when a new memory is stored |\n| `memory.updated` | Fired when a memory is updated |\n| `memory.deleted` | Fired when a memory is deleted |\n| `memory.verified` | Fired when a memory is verified |\n| `memory.unverified` | Fired when a memory is unverified |\n| `task.updated` | Fired when a scheduled task status changes |\n| `chat.message_sent` | Fired when a chat message is sent |\n\n## Create Webhook\n\n`POST /webhooks`\n\nCreate a new webhook for the authenticated mind.\n\n### Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `url` | string | yes | The URL to call when the event fires. Must be HTTPS (or `http://localhost` for development). |\n| `events` | string | yes | Comma-separated event patterns. Use `*` for all events. Examples: `memory.*`, `chat.*` |\n| `secret` | string | yes | Secret for HMAC-SHA256 signature verification. |\n\n### Response\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | string (UUID) | Webhook ID |\n| `url` | string | Webhook URL |\n| `events` | string | Event pattern (e.g. `memory.*`) |\n| `mind_id` | string (UUID) | Mind that owns this webhook |\n| `enabled` | boolean | Whether the webhook is enabled |\n| `created_at` | number | Unix timestamp (seconds) |\n\n## List Webhooks\n\n`GET /webhooks`\n\nLists all webhooks for the authenticated mind.\n\n### Response\n\nArray of webhook objects with the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | string (UUID) | Webhook ID |\n| `url` | string | Webhook URL |\n| `events` | string | Event pattern (e.g. `memory.*`) |\n| `mind_id` | string (UUID) | Mind that owns this webhook |\n| `enabled` | boolean | Whether the webhook is enabled |\n| `created_at` | number | Unix timestamp (seconds) |\n| `last_call` | number or null | Unix timestamp of last call |\n| `last_status` | number or null | HTTP status code of last call |\n| `last_error` | string or null | Error message from last call |\n\n## Get Webhook\n\n`GET /webhooks/:id`\n\nGets a single webhook by ID. Requires ownership.\n\n### Response\n\nSame fields as List.\n\n## Update Webhook\n\n`PUT /webhooks/:id`\n\nUpdate a webhook's URL, events, or secret. Requires ownership.\n\n### Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `url` | string | no | New webhook URL |\n| `events` | string | no | New event patterns |\n| `secret` | string | no | New secret |\n\n## Enable / Disable Webhook\n\n`POST /webhooks/:id/enable`\n\nEnable a webhook. Requires ownership.\n\n`POST /webhooks/:id/disable`\n\nDisable a webhook. Requires ownership.\n\n## Test Webhook\n\n`POST /webhooks/:id/test`\n\nSends a test event (`memory.created`) to the webhook URL. Requires ownership.\n\n## Delete Webhook\n\n`DELETE /webhooks/:id`\n\nDelete a webhook by ID. Requires ownership.\n\n## Example\n\n```bash\ncurl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H \"Authorization: Bearer YOUR_MIND_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"url\": \"https://example.com/webhook\",\n  \"events\": \"memory.*\",\n  \"secret\": \"your-webhook-secret\"\n}'\n```\n\n### Response\n\n```json\n{\n  \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"url\": \"https://example.com/webhook\",\n  \"events\": \"memory.*\",\n  \"mind_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n  \"enabled\": true,\n  \"created_at\": 1782756632\n}\n```\n\n## Signature Verification\n\nThe webhook payload includes an HMAC-SHA256 signature in the `X-Synapse-Signature` header. **The signature is computed over the raw request body bytes** (not parsed-then-restringified JSON).\n\n```python\nimport hashlib, hmac\n\n# IMPORTANT: Use the RAW request body bytes, not parsed JSON\npayload_body = request.get_data()  # Flask; or request.body in Django/FastAPI\n\nsig = hmac.new(\n    webhook_secret.encode(),\n    payload_body,\n    hashlib.sha256\n).hexdigest()\n\nexpected_header = f\"sha256={sig}\"\nactual_header = request.headers.get('X-Synapse-Signature', '')\nassert hmac.compare_digest(expected_header, actual_header)\n```\n\n## Limits & Auto-Disable\n\n- **Rate Limit**: 20 webhooks per mind.\n- **Auto-Disable**: A webhook returning HTTP 410 (Gone) is automatically disabled. This allows remote services to signal \"stop sending\" without deleting the webhook configuration.\n\n## Deprecated Event Names\n\n| Old Name | New Name |\n|----------|----------|\n| `memory.store` | `memory.created` |\n| `memory.update` | `memory.updated` |\n| `memory.delete` | `memory.deleted` |\n","content_html":"<h1>Webhooks</h1>\n<h2>Overview</h2>\n<p>Webhooks allow you to receive HTTP callbacks when events occur in your Synapse mind. When an event triggers, Synapse sends a <code>POST</code> request to the URL you configured, with a JSON body containing event data and a HMAC-SHA256 signature for verification.</p>\n<p>Webhooks have a <strong>20 webhooks per mind</strong> limit.</p>\n<h2>Events</h2>\n<table>\n<thead>\n<tr>\n<th>Event</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>memory.created</code></td>\n<td>Fired when a new memory is stored</td>\n</tr>\n<tr>\n<td><code>memory.updated</code></td>\n<td>Fired when a memory is updated</td>\n</tr>\n<tr>\n<td><code>memory.deleted</code></td>\n<td>Fired when a memory is deleted</td>\n</tr>\n<tr>\n<td><code>memory.verified</code></td>\n<td>Fired when a memory is verified</td>\n</tr>\n<tr>\n<td><code>memory.unverified</code></td>\n<td>Fired when a memory is unverified</td>\n</tr>\n<tr>\n<td><code>task.updated</code></td>\n<td>Fired when a scheduled task status changes</td>\n</tr>\n<tr>\n<td><code>chat.message_sent</code></td>\n<td>Fired when a chat message is sent</td>\n</tr>\n</tbody></table>\n<h2>Create Webhook</h2>\n<p><code>POST /webhooks</code></p>\n<p>Create a new webhook for the authenticated mind.</p>\n<h3>Request Body</h3>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>url</code></td>\n<td>string</td>\n<td>yes</td>\n<td>The URL to call when the event fires. Must be HTTPS (or <code>http://localhost</code> for development).</td>\n</tr>\n<tr>\n<td><code>events</code></td>\n<td>string</td>\n<td>yes</td>\n<td>Comma-separated event patterns. Use <code>*</code> for all events. Examples: <code>memory.*</code>, <code>chat.*</code></td>\n</tr>\n<tr>\n<td><code>secret</code></td>\n<td>string</td>\n<td>yes</td>\n<td>Secret for HMAC-SHA256 signature verification.</td>\n</tr>\n</tbody></table>\n<h3>Response</h3>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>id</code></td>\n<td>string (UUID)</td>\n<td>Webhook ID</td>\n</tr>\n<tr>\n<td><code>url</code></td>\n<td>string</td>\n<td>Webhook URL</td>\n</tr>\n<tr>\n<td><code>events</code></td>\n<td>string</td>\n<td>Event pattern (e.g. <code>memory.*</code>)</td>\n</tr>\n<tr>\n<td><code>mind_id</code></td>\n<td>string (UUID)</td>\n<td>Mind that owns this webhook</td>\n</tr>\n<tr>\n<td><code>enabled</code></td>\n<td>boolean</td>\n<td>Whether the webhook is enabled</td>\n</tr>\n<tr>\n<td><code>created_at</code></td>\n<td>number</td>\n<td>Unix timestamp (seconds)</td>\n</tr>\n</tbody></table>\n<h2>List Webhooks</h2>\n<p><code>GET /webhooks</code></p>\n<p>Lists all webhooks for the authenticated mind.</p>\n<h3>Response</h3>\n<p>Array of webhook objects with the following fields:</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>id</code></td>\n<td>string (UUID)</td>\n<td>Webhook ID</td>\n</tr>\n<tr>\n<td><code>url</code></td>\n<td>string</td>\n<td>Webhook URL</td>\n</tr>\n<tr>\n<td><code>events</code></td>\n<td>string</td>\n<td>Event pattern (e.g. <code>memory.*</code>)</td>\n</tr>\n<tr>\n<td><code>mind_id</code></td>\n<td>string (UUID)</td>\n<td>Mind that owns this webhook</td>\n</tr>\n<tr>\n<td><code>enabled</code></td>\n<td>boolean</td>\n<td>Whether the webhook is enabled</td>\n</tr>\n<tr>\n<td><code>created_at</code></td>\n<td>number</td>\n<td>Unix timestamp (seconds)</td>\n</tr>\n<tr>\n<td><code>last_call</code></td>\n<td>number or null</td>\n<td>Unix timestamp of last call</td>\n</tr>\n<tr>\n<td><code>last_status</code></td>\n<td>number or null</td>\n<td>HTTP status code of last call</td>\n</tr>\n<tr>\n<td><code>last_error</code></td>\n<td>string or null</td>\n<td>Error message from last call</td>\n</tr>\n</tbody></table>\n<h2>Get Webhook</h2>\n<p><code>GET /webhooks/:id</code></p>\n<p>Gets a single webhook by ID. Requires ownership.</p>\n<h3>Response</h3>\n<p>Same fields as List.</p>\n<h2>Update Webhook</h2>\n<p><code>PUT /webhooks/:id</code></p>\n<p>Update a webhook&#39;s URL, events, or secret. Requires ownership.</p>\n<h3>Request Body</h3>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>url</code></td>\n<td>string</td>\n<td>no</td>\n<td>New webhook URL</td>\n</tr>\n<tr>\n<td><code>events</code></td>\n<td>string</td>\n<td>no</td>\n<td>New event patterns</td>\n</tr>\n<tr>\n<td><code>secret</code></td>\n<td>string</td>\n<td>no</td>\n<td>New secret</td>\n</tr>\n</tbody></table>\n<h2>Enable / Disable Webhook</h2>\n<p><code>POST /webhooks/:id/enable</code></p>\n<p>Enable a webhook. Requires ownership.</p>\n<p><code>POST /webhooks/:id/disable</code></p>\n<p>Disable a webhook. Requires ownership.</p>\n<h2>Test Webhook</h2>\n<p><code>POST /webhooks/:id/test</code></p>\n<p>Sends a test event (<code>memory.created</code>) to the webhook URL. Requires ownership.</p>\n<h2>Delete Webhook</h2>\n<p><code>DELETE /webhooks/:id</code></p>\n<p>Delete a webhook by ID. Requires ownership.</p>\n<h2>Example</h2>\n<pre><code class=\"hljs language-bash\">curl -X POST https://synapse.schaefer.zone/webhooks \\\n  -H <span class=\"hljs-string\">&quot;Authorization: Bearer YOUR_MIND_KEY&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/webhook&quot;,\n  &quot;events&quot;: &quot;memory.*&quot;,\n  &quot;secret&quot;: &quot;your-webhook-secret&quot;\n}&#x27;</span></code></pre><h3>Response</h3>\n<pre><code class=\"hljs language-json\"><span class=\"hljs-punctuation\">{</span>\n  <span class=\"hljs-attr\">&quot;id&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">&quot;550e8400-e29b-41d4-a716-446655440000&quot;</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">&quot;url&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">&quot;https://example.com/webhook&quot;</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">&quot;events&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">&quot;memory.*&quot;</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">&quot;mind_id&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-string\">&quot;a1b2c3d4-e5f6-7890-abcd-ef1234567890&quot;</span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">&quot;enabled&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-literal\"><span class=\"hljs-keyword\">true</span></span><span class=\"hljs-punctuation\">,</span>\n  <span class=\"hljs-attr\">&quot;created_at&quot;</span><span class=\"hljs-punctuation\">:</span> <span class=\"hljs-number\">1782756632</span>\n<span class=\"hljs-punctuation\">}</span></code></pre><h2>Signature Verification</h2>\n<p>The webhook payload includes an HMAC-SHA256 signature in the <code>X-Synapse-Signature</code> header. <strong>The signature is computed over the raw request body bytes</strong> (not parsed-then-restringified JSON).</p>\n<pre><code class=\"hljs language-python\"><span class=\"hljs-keyword\">import</span> hashlib, hmac\n\n<span class=\"hljs-comment\"># IMPORTANT: Use the RAW request body bytes, not parsed JSON</span>\npayload_body = request.get_data()  <span class=\"hljs-comment\"># Flask; or request.body in Django/FastAPI</span>\n\nsig = hmac.new(\n    webhook_secret.encode(),\n    payload_body,\n    hashlib.sha256\n).hexdigest()\n\nexpected_header = <span class=\"hljs-string\">f&quot;sha256=<span class=\"hljs-subst\">{sig}</span>&quot;</span>\nactual_header = request.headers.get(<span class=\"hljs-string\">&#x27;X-Synapse-Signature&#x27;</span>, <span class=\"hljs-string\">&#x27;&#x27;</span>)\n<span class=\"hljs-keyword\">assert</span> hmac.compare_digest(expected_header, actual_header)</code></pre><h2>Limits &amp; Auto-Disable</h2>\n<ul>\n<li><strong>Rate Limit</strong>: 20 webhooks per mind.</li>\n<li><strong>Auto-Disable</strong>: A webhook returning HTTP 410 (Gone) is automatically disabled. This allows remote services to signal &quot;stop sending&quot; without deleting the webhook configuration.</li>\n</ul>\n<h2>Deprecated Event Names</h2>\n<table>\n<thead>\n<tr>\n<th>Old Name</th>\n<th>New Name</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>memory.store</code></td>\n<td><code>memory.created</code></td>\n</tr>\n<tr>\n<td><code>memory.update</code></td>\n<td><code>memory.updated</code></td>\n</tr>\n<tr>\n<td><code>memory.delete</code></td>\n<td><code>memory.deleted</code></td>\n</tr>\n</tbody></table>\n","urls":{"html":"/docs/api/webhooks","text":"/docs/api/webhooks?format=text","json":"/docs/api/webhooks?format=json","llm":"/docs/api/webhooks?format=llm"},"translations_available":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"]}