> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sovseal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Configure external webhook endpoints for metadata sync events.

| Deployment Model | Availability |
| :--------------- | :----------- |
| **Platform**     | ✓ Available  |
| **Self-Hosted**  | ✓ Available  |

> **Honesty Ledger (Provenance Layer)**: Webhooks fire based on events at the **Server-known (Layer A)** layer.
>
> **ZK Trust Boundary**:
>
> * **What the server sees**: Webhook configuration, delivery counts, target URLs, and response status codes.
> * **What stays on device**: All plaintext memories and query strings.

***

## Webhooks

Webhooks allow your external applications to receive real-time notifications for synchronization and security events. Because the server is blind, webhook events never contain plaintext memory content or query details.

### Subscribed Events Catalog

You can configure webhook subscriptions for the following metadata-only events:

* `sync.completed`: Triggers when an agent successfully pushes a batch of encrypted envelopes to the replication server.
* `key.revoked`: Triggers when a sub-key or API key is revoked.
* `quota.hit`: Triggers when the project approaches or exceeds plan limits.
* `retention.purged`: Triggers when stale snapshots are purged under retention rules.

### HMAC Signature Verification

To protect webhook endpoints from spoofing, each HTTP POST payload is signed with an HMAC-SHA256 signature, transmitted in the header: `X-Sovseal-Signature: t=2026-06-10T11:12:05Z,v1=sha256_hash`

You can verify the payload in Node.js:

```ts theme={null}
import crypto from "crypto";

function verifyWebhook(payload: string, signatureHeader: string, secret: string): boolean {
  const [tPart, vPart] = signatureHeader.split(",");
  const timestamp = tPart?.split("=")[1];
  const signature = vPart?.split("=")[1];

  const signingString = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(signingString)
    .digest("hex");

  return expectedSignature === signature;
}
```

### Metadata-Only Payload Example

Under zero-knowledge constraints, webhook payloads contain strictly **zero cleartext content**:

```json theme={null}
{
  "event": "sync.completed",
  "project_token": "sov_proj_98c61a4f-015d-4f10-9b43-28956ae779bb",
  "timestamp": "2026-06-10T11:12:05Z",
  "delivery_id": "dlv-8fb2a4d7c",
  "data": {
    "sync_ops_used": 165,
    "total_bytes_encrypted": 156800,
    "connected_devices": 2,
    "last_sync_latency_ms": 6.3
  }
}
```
