Skip to main content
Phase 5 ships two complementary surfaces for reactive observability of canonical card changes:
  • Server-Sent Events at GET /v1/agents/{id}/stream — long-lived HTTP connection; consumers receive a card_changed SSE frame whenever the agent’s canonical card recomposes.
  • Signed webhook subscriptions at POST /v1/agents/{id}/notifications/webhook — Mnemom POSTs a signed payload to your URL on each canonical change.
Both surfaces consume the transparency log as their event source. Subscriptions are per-agent + opt-in: the agent’s administrator flips agents.sse_enabled / agents.webhook_enabled first.

SSE channel

Connect

The response is Content-Type: text/event-stream. Each canonical change emits a frame like:

Reconnect with cursor

The id field on each frame is the transparency-log log_index. On reconnect, pass it as Last-Event-ID (SSE convention) or as the since query parameter:
Frames are delivered strictly in log_index ASC order, so the cursor is monotone.

Stream lifetime

Connections cap at 5 minutes (Cloudflare Workers HTTP response budget). Clients reconnect with the latest cursor — the SSE convention handles this automatically in EventSource. A : keepalive <timestamp> comment frame is emitted every 15 seconds; a final event: close frame fires before the connection drops.

Authentication

Currently the endpoint is unauthenticated when the per-agent flag is on — the same access pattern as the A2A AgentCard export. If the per-agent flag is off the endpoint returns 404 (no enumeration).

Webhook channel

Subscribe

Response:
The secret is shown exactly once — store it server-side. Mnemom holds only the hash; subsequent verifications use the hash.

Delivery shape

On every canonical card change, Mnemom POSTs to your URL:
With headers:

Verify the signature

The HMAC-SHA256 signature is computed over the string <timestamp>.<raw-body>:

Idempotent delivery

Each subscription tracks last_sent_log_index. If the compose hook reruns the same log entry (e.g., the reconciler closes a gap that the compose path also closed), the dispatcher skips re-delivery for any subscription whose last_sent_log_index >= log_index.

Unsubscribe

Returns 204 on success, 404 if the subscription is already gone (idempotent).

Opt-in

By default, both flags are off:
Without the flag flipped, both endpoints return 404 (avoids enumeration of which agents exist).

Webhook URL constraints

  • https:// only. http URLs are declined.
  • No loopback / private-network space (10/8, 172.16/12, 192.168/16, 169.254/16, fc00::/7, fe80::/10).
  • No .local / .internal TLDs.
These constraints defend against SSRF on outbound delivery. If your dev environment would benefit from a tunnel, ngrok or cloudflared work cleanly.

SSE vs webhook — which to use

Both surfaces deliver the same payload from the same event source (the transparency log). Choosing both is fine — they don’t conflict.

See also