Agent Memory

Webhooks and change feed

Push memory changes to external systems with signed webhooks, or pull them from a cursor-based change feed.

You can send memory changes to external systems in two ways:

MethodWhen?
WebhookYour system needs to know immediately when something changes (push).
Change feed (changesets)You want to ask periodically "what changed since I last read?" (pull). Suited to auditing and sync.

Neither carries content: a webhook contains a short, masked summary, and the feed contains only policy fields. For details, read the memory by its ID.

Webhooks

Subscriptions are managed on the console's Webhooks page (your plan must include the webhook feature). A subscription consists of a name, an HTTPS URL and a list of events; when it is created, a secret (whsec_…) is shown once.

Events

EventWhen?
addA new memory was written.
updateA memory's content was updated or changed during a merge.
deleteA memory was deleted.
categorizeA run went through consolidation.
testThe "Test" button in the console (memoryId is empty).

Memories produced by consolidation don't emit individual add events; you get a single categorize for the run. Memories forgotten because they expired don't emit events.

Delivery

Each delivery is a POST request:

POST /hooks/agent-memory HTTP/1.1
Content-Type: application/json
X-AgentMemory-Signature: t=1792315107,v1=5f2c…
X-AgentMemory-Event: add
X-AgentMemory-Delivery: 3b6e…

{"version":"1","event":"add","eventId":"…","memoryId":"…","tenantId":"…","workspaceId":"…",
 "applicationId":null,"summary":"Invoice numbers use a year prefix …","occurredAt":"2026-10-18T09:18:36.88+00:00"}
  • summary is at most 256 characters, with personal data masked; the full content is not sent.
  • eventId is the same across deliveries of the same event to all subscriptions; X-AgentMemory-Delivery is specific to the delivery chain and does not change across retries.
  • Return 2xx within 10 seconds; do heavy work in the background.
  • Failed deliveries are retried at increasing intervals (1 min, 5 min, 30 min, 2 h); after 5 attempts a delivery is considered "dead". You can requeue dead deliveries from the console. Delivery history is kept for 30 days.
  • The target URL goes through a security check: redirects are not followed, and nothing is delivered to internal network addresses.

Verifying signatures

The signature is the HMAC-SHA256 digest (lowercase hex) of the string "{t}.{raw body}", keyed with the secret.

import hmac, hashlib, time

def verify(raw_body: bytes, signature_header: str, secrets: list[str], tolerance=300) -> bool:
    parts = [p.split("=", 1) for p in signature_header.split(",")]
    t = next(v for k, v in parts if k == "t")
    candidates = [v for k, v in parts if k == "v1"]
    if abs(time.time() - int(t)) > tolerance:
        return False
    message = f"{t}.".encode() + raw_body
    for secret in secrets:
        expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
        if any(hmac.compare_digest(expected, c) for c in candidates):
            return True
    return False
  • Verify the raw body, before parsing it as JSON.
  • When you rotate the secret, every delivery carries two v1 signatures for 24 hours (new first, then old); accept both secrets during that window.
  • Skip duplicates by storing processed X-AgentMemory-Delivery IDs for a short time.

Change feed

curl -s "$API/v1/memory/changesets?workspaceId=$WS&limit=50" -H "Authorization: Bearer $KEY"
{
  "rows": [
    {"seq":"8149","id":"…","memoryId":"…","action":"create","actor":"apikey:…","actorRole":"agent",
     "oldValues":{},"newValues":{"type":"decision","visibility":"workspace"},"reason":null,
     "createdAt":"2026-10-18T09:18:36.88+00:00"}
  ],
  "nextCursor": "8151",
  "hasMore": true
}
ParameterDescription
actioncreate, update, delete, promote, share, policy_change
memoryIdChanges to a single memory.
sinceChanges after this time.
cursorThe nextCursor from the previous response. Store it and pass it on the next call to get only new changes.
limit1–500 (default 50).

Rows are ordered oldest to newest. A delete row also lists dependent memories flagged with cascade. Deletions caused by expiry and retention appear with the system:retention actor.

MCP: get_changesets(action?, memoryId?, cursor?, limit).

On this page