mem9 webhooks let external systems react to memory lifecycle and Space Chain routing events without polling. The implementation keeps mem9 as the source of truth: endpoint configuration, signing secrets, event records, delivery outbox, delivery attempts, retry state, and logs all live in the mem9 control-plane database.
Console products only proxy and aggregate this API. They must not persist webhook endpoint state or delivery state.
- Support Space-scoped webhook endpoints authenticated with the existing
X-API-Key. - Support Space Chain-scoped webhook endpoints authenticated with the
chain_management key. - Emit v1 events only:
memory.addedmemory.deletedspace_chain.fact_routed
- Deliver signed JSON payloads to HTTPS URLs. Local HTTP is allowed only outside production.
- Retry transient delivery failures with backoff and preserve delivery history.
- Return
signing_secretonly on create and rotate-secret responses.
Each event is stored once in webhook_events and faned out to enabled matching endpoints through webhook_deliveries.
{
"id": "evt_...",
"type": "memory.added",
"api_version": "2026-06-08",
"created_at": "2026-06-08T00:00:00Z",
"scope": {
"type": "tenant",
"tenant_id": "...",
"external_space_id": "...",
"chain_id": "..."
},
"data": {}
}Emitted after a memory create path succeeds:
- direct content create
- pinned create
- smart ingest ADD action
- routed Space Chain target ADD action
Smart ingest UPDATE actions do not emit memory.added.
Payload:
{
"memory": {
"id": "...",
"content": "...",
"memory_type": "insight",
"agent_id": "...",
"appId": "...",
"session_id": "...",
"tags": [],
"metadata": {},
"created_at": "...",
"updated_at": "..."
}
}Emitted after a single or batch delete succeeds.
Payload:
{
"memory": {
"id": "...",
"tenant_id": "...",
"deleted_by_agent": "...",
"deleted_at": "..."
}
}Emitted to the Space Chain scope when extracted facts match a target Space routing policy and the target Space write succeeds. Quota-denied or failed target writes do not emit this event.
Payload:
{
"route_id": "...",
"chain_id": "...",
"source_tenant_id": "...",
"target_tenant_id": "...",
"target_external_space_id": "...",
"routing_policy_node_id": "...",
"source_facts": [],
"target_memory": {},
"agent_id": "...",
"appId": "...",
"session_id": "..."
}Space scope, using X-API-Key:
GET /v1alpha2/mem9s/webhooksPOST /v1alpha2/mem9s/webhooksGET /v1alpha2/mem9s/webhooks/{webhookID}PATCH /v1alpha2/mem9s/webhooks/{webhookID}DELETE /v1alpha2/mem9s/webhooks/{webhookID}POST /v1alpha2/mem9s/webhooks/{webhookID}/testPOST /v1alpha2/mem9s/webhooks/{webhookID}/rotate-secretGET /v1alpha2/mem9s/webhook-deliveries
Space Chain scope, using chain_ management key:
GET /v1alpha2/space-chains/{chainID}/webhooksPOST /v1alpha2/space-chains/{chainID}/webhooksGET /v1alpha2/space-chains/{chainID}/webhooks/{webhookID}PATCH /v1alpha2/space-chains/{chainID}/webhooks/{webhookID}DELETE /v1alpha2/space-chains/{chainID}/webhooks/{webhookID}POST /v1alpha2/space-chains/{chainID}/webhooks/{webhookID}/testPOST /v1alpha2/space-chains/{chainID}/webhooks/{webhookID}/rotate-secretGET /v1alpha2/space-chains/{chainID}/webhook-deliveries
Create request:
{
"name": "Production sync",
"url": "https://example.com/mem9/webhook",
"enabled": true,
"events": ["memory.added", "memory.deleted"]
}Create and rotate-secret responses include one-time signing_secret; list/get/update responses never include it.
Each delivery includes:
X-Mem9-Event-IdX-Mem9-Event-TypeX-Mem9-TimestampX-Mem9-Signature
Signature format:
v1=<hex_hmac_sha256>
The HMAC input is:
<timestamp>.<event_id>.<raw_body>
Consumers should verify the timestamp tolerance, recompute with the current signing secret, and compare in constant time.
- Production requires HTTPS.
- Development allows HTTP only for
localhost,127.0.0.1, and::1. - Userinfo URLs are rejected.
- Literal private, link-local, loopback, and unspecified IP destinations are rejected unless the local-development exception applies.
webhook_endpoints
- endpoint metadata
- scope type and ID
- event subscriptions as JSON
- encrypted signing secret
- soft-delete marker
webhook_events
- immutable event envelope
- scope and type for filtering
webhook_deliveries
- endpoint fanout rows
- status, attempt count, retry schedule
- latest HTTP status or error
- delivered timestamp
stateDiagram-v2
[*] --> pending
pending --> delivered: 2xx response
pending --> pending: failed attempt, attempts remain
pending --> failed: max attempts reached
Default backoff:
- attempt 1: 1 minute
- attempt 2: 5 minutes
- attempt 3: 30 minutes
- attempt 4: 2 hours
- attempt 5: 6 hours
- later attempts: 24 hours
Space webhooks are bound to a tenant/Space API key and only observe that Space. Space Chain webhooks are bound to a chain management key and observe chain-scope events. When a chain write creates a memory in a target Space, mem9 emits the target Space memory.added event and the chain memory.added event. Routing-specific metadata is emitted separately as space_chain.fact_routed.
mem9-console-server proxies webhook CRUD/test/rotate/delivery calls to mem9 using the current Space API key or Chain management key. It also exposes project-level aggregation endpoints by reading the console project Spaces and Space Chains, then calling mem9 for each resource.
mem9-console-fe renders a project-level /console/webhooks page from those aggregation APIs. It never calls mem9 directly and never stores webhook secrets.
mem9:
- CRUD endpoint validation and one-time secret responses.
- secret rotation and HMAC signing.
- URL validation including HTTPS and local development exceptions.
memory.addedfor direct, pinned, and smart ADD paths.- no
memory.addedfor smart UPDATE-only reconciliation. memory.deletedfor single and batch delete.space_chain.fact_routedfor successful routed target ADDs.- no routing event when runtime usage denies or target write fails.
- dispatcher success, retry, backoff, and terminal failure.
console-server:
- Space and Space Chain proxy auth and error mapping.
- project aggregation limited to current principal/project resources.
- one-time secret passthrough with no logging.
- no webhook DB migration.
console-fe:
- sidebar entry order: Space, Space Chain, Webhooks, Memories.
- create/edit/test/rotate/disable/delete flows.
- one-time secret reveal modal.
- deliveries drawer/table.
- generated API client is used for all webhook requests.