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

# Webhook Contract

> Seven invariants every Mnemom webhook event carries — stable name, JSON Schema, HMAC signature, idempotency key, retry policy, replay path, surface separation.

Mnemom webhook events are **operator-actionable signals**. Every event the platform emits — whether a billing event (`subscription.status_changed`), a Safe House signal (`integrity.violation`), or a sideband detection firing (`sideband.coherence.fired`) — carries the same seven contractual guarantees.

This page is the rationale for those guarantees and how to verify them. The full event catalog is at [Webhook Event Catalog](/api-reference/webhook-events).

## The seven invariants

### 1. Stable name

Every event is identified by a hierarchical, dotted name (`<context>.<axis>[.<event>]`). The name is **stable across versions** — if the underlying mechanism changes, the existing name continues to work; the new mechanism gets a new name.

The name space is a closed, versioned enum, published in full at the [Webhook Event Catalog](/api-reference/webhook-events). A new event is added to the catalog atomically with its schema and its emission code — the platform's own CI fails a change that adds one without the other.

### 2. Versioned JSON Schema

Every event has a JSON Schema, rendered live on [docs.mnemom.ai/api-reference/webhook-events](/api-reference/webhook-events) and exported as OpenAPI for client generation.

Payload evolution is **additive** — new fields can be added without bumping the event name. Breaking changes bump the catalog version (date-based, mirroring `X-Mnemom-Version`).

### 3. Example payload

Every event in the catalog ships with an example payload — the canonical reference for client implementations. Test delivery end-to-end without waiting for a real event with `mnemom webhooks trigger <org_id> <endpoint_id>`, which fires a synthetic test event through the full delivery pipeline to that endpoint.

### 4. Idempotency key

Every event carries a stable `id` field in the form `evt-<random>`. **Receivers can safely dedupe on this id** — Mnemom guarantees the same `id` will not be issued for two different events.

When Mnemom retries delivery (per the retry policy below), the same `id` flows through every attempt. Your receiver can store seen ids and short-circuit re-emissions without losing semantic correctness.

### 5. HMAC signature

Every delivery is signed HMAC-SHA256 over `${X-Webhook-Timestamp}.${rawBody}` using the per-endpoint signing secret, sent as `X-Webhook-Signature: v1=<hex>` (Stripe convention), with a 5-minute replay tolerance. See [Signature verification](/guides/webhooks#signature-verification) for runnable verification code in Node and Python.

### 6. Retry policy

Mnemom retries failed deliveries on this schedule (in seconds): `[10, 30, 120, 600, 3600]` (5 retries, 6 attempts total). Receivers that return any 2xx within 30 seconds are considered successful. 5xx and connection failures are retried; 4xx (other than 408/429) are NOT retried — they indicate a permanent receiver-side error.

After 100 consecutive failures across all events to an endpoint, Mnemom auto-disables the endpoint and emails the org owner. The endpoint stays disabled until manually re-enabled.

The first-attempt timeout is **30 seconds** (mirroring Stripe / Cloudflare receiver-budget conventions). Receivers with database writes or downstream fan-out should write the event to a queue first and ack immediately.

### 7. Replay path

Every emitted event is durably stored in `webhook_events` and can be replayed via `POST /v1/orgs/:org_id/webhooks/events/:event_id/replay`. Replay re-fans-out the event to all currently-subscribed active endpoints (or to a caller-specified subset via `endpoint_ids[]` in the request body).

Replay differs from `redeliver`:

* **`replay`** rebuilds the fan-out from the canonical event row — picks up endpoints that have been added or re-subscribed since the original emission.
* **`redeliver`** retries one specific delivery row.

Both shapes ship — Stripe-equivalent ergonomics. See the [webhook event catalog](/api-reference/webhook-events) for the full API surface.

The replay endpoint requires an `Idempotency-Key` header; reuse with the same key returns the cached delivery list (does NOT mint a second fan-out). Cached replays carry an `Idempotent-Replay: true` response header.

## Surface separation invariant

Mnemom webhook events live exclusively on the **operator surface**.

Operators are **humans, dashboards, paging systems, automated systems acting outside an agent's request loop**. Agent-actionable signals (per-turn, conversation-bound, where the agent has a same-turn lever) belong to `pending_advisories` only and have no webhook fan-out.

This is enforced at three layers:

1. **Schema metadata.** Every catalog entry declares `x-mnemom-surface: operator-actionable`. Anything else fails the CI lint.
2. **Producer-layer separation.** `pending_advisories` accepts only `runtime.*` + `manual.*` source values; `governance_signals` accepts `sideband.*` + future `protection.*` / `posture.*`. The two surfaces are mutually exclusive.
3. **CI assertion.** Every emitted event has a negative-assertion test confirming that when the event fires, the agent's verify-turn prompt remains byte-clean (no leak from operator surface to agent prompt).

## Scope: which deliveries this contract covers

This contract governs the **org webhook subscription system**: the closed set of catalog events, delivered to endpoints you register at `POST /v1/orgs/:org_id/webhooks`. It is the delivery mechanism for `sideband.*.fired`, `recipe.candidate.created`, billing events, and the rest of the [catalog](/api-reference/webhook-events) — see that page's note on which detection-recipe lifecycle events stay internal to Mnemom today.

[Governance signal](/concepts/governance-signals) notification destinations (`governance.signal.fired` and its lifecycle siblings) are a **separate, purpose-built delivery path** configured per-org under `/v1/orgs/:org_id/governance/notification-destinations` — not an entry in this catalog. It uses its own signature header (`X-Mnemom-Signature: sha256=<hex>` over the raw body) and its own delivery semantics. Don't assume this page's retry schedule, idempotency key format, or `X-Webhook-Signature` scheme apply there.

## What the contract is not

* **Not at-most-once delivery.** Mnemom delivers at-least-once, with idempotency keys for receiver-side dedup. Build your receiver to handle a small number of duplicates.
* **Not strict ordering.** Events are emitted in the order they occur, but delivery to a slow receiver may arrive out of order if a retry races a fresh delivery. Use the event payload's `created_at` for canonical ordering.
* **Not exactly-once semantics.** Combined with idempotency keys, you can build exactly-once semantics on the receiver — but the wire contract is at-least-once.
* **Not customer-tunable retry.** The retry schedule is platform-wide. If you need different retry behavior, dead-letter to a queue at your receiver and process there.

## See also

* [Webhook Event Catalog](/api-reference/webhook-events) — every event, with schemas + examples.
* [Headers reference](/api-reference/headers) — the canonical Mnemom-namespaced response set.
* [Governance signals](/concepts/governance-signals) — the operator-actionable observation surface and surface separation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.