Skip to Content
AgentMem is in active development. Star us on GitHub.
FeaturesWebhooks

Webhooks

Subscribe to events fired by the API. Useful for keeping your own systems in sync (analytics, downstream caches, audit trails) without polling.

Event types

EventWhen it fires
memory.writtenA new memory was successfully stored
memory.deletedA memory was soft-deleted via DELETE /v1/memory/:id
memory.expiredThe TTL sweep soft-deleted a memory whose expires_at passed
memory.conflict_blockedA write was blocked by planner_wins or human_in_loop
memory.conflict_detectedA high-severity conflict was detected by human_in_loop
memory.supersededOne or more memories were soft-deleted by timestamp_wins

Subscribe

const { secret, ...sub } = await mem.createWebhook({ url: 'https://your-backend.example.com/agentmem/hook', events: ['memory.written', 'memory.expired', 'memory.conflict_detected'], }) console.log('SAVE THIS:', secret) // The signing secret is shown ONCE. Store it; we don't return it again.

The subscription is active immediately. Disable or re-enable via:

await mem.disableWebhook(sub.id) await mem.enableWebhook(sub.id) await mem.deleteWebhook(sub.id)

Payload shape

Every event has the same envelope:

{ "event": "memory.written", "orgId": "uuid", "payload": { /* event-specific */ }, "timestamp": "2026-05-14T10:00:00Z" }

The payload differs per event. Examples:

// memory.written { "writeId": "uuid", "agentId": "support-bot", "scope": "team", "workflowId": "wf-123", "encrypted": false } // memory.expired { "memoryId": "uuid", "agentId": "support-bot" } // memory.conflict_detected { "conflicts": [{ "memoryId": "...", "content": "...", "severity": "high", "description": "..." }] } // memory.superseded { "agentId": "...", "supersededIds": ["uuid1", "uuid2"] }

Verifying signatures

Every delivery is HMAC-SHA256 signed using the secret you saved at subscription time. The signature is in the X-AgentMem-Signature header as sha256=<hex>.

Sample Node.js verification:

import crypto from 'node:crypto' function verify(rawBody: string, signatureHeader: string, secret: string): boolean { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex') return crypto.timingSafeEqual( Buffer.from(signatureHeader), Buffer.from(expected), ) }

Important: sign over the raw request body, not the parsed JSON. Re-stringifying the JSON before computing the HMAC will produce a different signature (whitespace, key ordering) and reject every event.

Delivery semantics

  • Fire-and-forget. AgentMem POSTs your URL once, in parallel for all matching subscribers.
  • No retries in v1. If your endpoint is down, the event is dropped.
  • No ordering guarantee across events.
  • 5-second timeout per delivery before AgentMem moves on.

Build your handlers to be idempotent (use the writeId / memoryId as a dedup key if you re-process), and don’t rely on receiving every event for correctness.

Listing subscriptions

const webhooks = await mem.listWebhooks() for (const w of webhooks) { console.log(w.id, w.url, w.events, w.active) }

The signing secret is not included in list responses — it’s only ever returned at creation time.

Last updated on