Problem
The snapshot/log foundation contract (#4582, snapshot-and-log.mdx) Rule 1 introduces a per-state-event identifier:
notification_id — event-layer, per state event. Stable across re-emissions of the same logical event. For state-shaped events this equals the resource's stable id (e.g., impairment_id is the notification_id for impairment events).
Receivers MUST track both idempotency_key (per-fire, transport-layer dedup) and notification_id (per-state, application-layer state correlation).
But notification_id is documented only in prose. The webhook envelope schema (core/mcp-webhook-payload.json / core/push-notification-config.json) doesn't declare it as a typed field. Receivers building strictly from JSON Schema — generated SDKs, conformance test fixtures, type-driven receivers — won't see notification_id in their type definitions. They'll have to read prose to know it exists.
The current webhooks.mdx persistent-channel contract hedges this:
Receivers MUST dedupe by idempotency_key (or the equivalent event-scoped id surfaced in the payload — e.g., notification_id for notification-type events).
"Or the equivalent event-scoped id surfaced in the payload" is the tell — the contract is documented but not typed.
Proposal
Add notification_id as a typed field on the webhook envelope shape, alongside the existing idempotency_key. Two ways to do this:
(a) Add to the existing envelope schema as an optional top-level field. Webhooks for non-state-event types (delivery reports today) omit it; webhooks for state events (impairment, future creative lifecycle, etc.) populate it. Simplest.
(b) Define a typed envelope shape per notification_type value via a discriminator. Stricter but verbose.
Going with (a) is the smaller spec lift and matches how task_id, operation_id are already surfaced today.
Where this lands
Most likely in core/mcp-webhook-payload.json and/or a sibling envelope schema referenced from there. Need to verify which schema is the canonical envelope (current state: webhooks.mdx documents the shape inline but the schema layer is split across mcp-webhook-payload, push-notification-config, and the per-task webhook payload schemas).
Backwards compatibility
- New field is optional; absent for fires that don't carry per-state semantics.
- Receivers that don't read it continue to dedupe on
idempotency_key as before.
Why this matters
Without typing, generated SDKs don't surface notification_id at the type level. Buyer agents calling webhook.notification_id would get a missing-property error or have to access via dynamic property lookup. That's a soft contract — receivers built strictly from generated types won't comply with the Rule 1 dedupe/correlate split unless they also read the prose.
Filing as a follow-up to #4588 (which landed the prose contract) and #2856 (which adds impairment to notification-type and is the natural place to plumb the envelope field through).
Acceptance
Related
Problem
The snapshot/log foundation contract (#4582,
snapshot-and-log.mdx) Rule 1 introduces a per-state-event identifier:Receivers MUST track both
idempotency_key(per-fire, transport-layer dedup) andnotification_id(per-state, application-layer state correlation).But
notification_idis documented only in prose. The webhook envelope schema (core/mcp-webhook-payload.json/core/push-notification-config.json) doesn't declare it as a typed field. Receivers building strictly from JSON Schema — generated SDKs, conformance test fixtures, type-driven receivers — won't seenotification_idin their type definitions. They'll have to read prose to know it exists.The current webhooks.mdx persistent-channel contract hedges this:
"Or the equivalent event-scoped id surfaced in the payload" is the tell — the contract is documented but not typed.
Proposal
Add
notification_idas a typed field on the webhook envelope shape, alongside the existingidempotency_key. Two ways to do this:(a) Add to the existing envelope schema as an optional top-level field. Webhooks for non-state-event types (delivery reports today) omit it; webhooks for state events (impairment, future creative lifecycle, etc.) populate it. Simplest.
(b) Define a typed envelope shape per
notification_typevalue via a discriminator. Stricter but verbose.Going with (a) is the smaller spec lift and matches how
task_id,operation_idare already surfaced today.Where this lands
Most likely in
core/mcp-webhook-payload.jsonand/or a sibling envelope schema referenced from there. Need to verify which schema is the canonical envelope (current state: webhooks.mdx documents the shape inline but the schema layer is split across mcp-webhook-payload, push-notification-config, and the per-task webhook payload schemas).Backwards compatibility
idempotency_keyas before.Why this matters
Without typing, generated SDKs don't surface
notification_idat the type level. Buyer agents callingwebhook.notification_idwould get a missing-property error or have to access via dynamic property lookup. That's a soft contract — receivers built strictly from generated types won't comply with the Rule 1 dedupe/correlate split unless they also read the prose.Filing as a follow-up to #4588 (which landed the prose contract) and #2856 (which adds
impairmenttonotification-typeand is the natural place to plumb the envelope field through).Acceptance
notification_idfield with description matching snapshot-and-log Rule 1Related
impairmentnotification_type (natural place to plumb the envelope field).