{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/core/push-notification-config.json",
  "title": "Push Notification Config",
  "description": "Webhook configuration for asynchronous task notifications. Uses A2A-compatible PushNotificationConfig structure. By default, webhooks are signed with the AdCP RFC 9421 profile (see docs/building/by-layer/L1/security.mdx#webhook-callbacks) — the seller signs outbound with a key published at the jwks_uri on its own brand.json `agents[]` entry and the buyer verifies against that JWKS, so no shared secret crosses the wire. The optional `authentication` block carries credentials for the legacy Bearer or HMAC-SHA256 fallback (deprecated; removed in AdCP 4.0). Note: the `idempotency_key` that receivers dedup on lives inside the webhook **payload** body (see docs/building/by-layer/L3/webhooks.mdx#reliability and the mcp-webhook-payload schema), not in this configuration object — this schema only describes the receiver-side transport config sent to the seller. This schema is designed for composition via allOf - consuming schemas should define their own additionalProperties constraints.",
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "Webhook endpoint URL for task status notifications. The wire contract is unconstrained beyond `format: \"uri\"` — in particular, publishers SHOULD NOT enforce a destination-port allowlist by default, since buyers legitimately host receivers on non-standard TLS ports (`:9443`, `:4443`, path-routed multi-tenant gateways). The SSRF guard the protocol relies on is the IP-range check + DNS-rebinding-resistant connect pin defined in [Webhook URL validation (SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf), not port filtering. Operators who want a hardened destination-port allowlist as defense-in-depth (e.g., locked-down enterprise egress) opt in explicitly — see [Destination port: permissive by default](/docs/building/by-layer/L1/security#destination-port-permissive-by-default)."
    },
    "operation_id": {
      "type": "string",
      "description": "Buyer-supplied correlation identifier for the operation that will produce webhooks against this registration. The seller MUST echo this value verbatim into every webhook payload's `operation_id` field (see [`mcp-webhook-payload.json`](/schemas/core/mcp-webhook-payload.json) and [Webhooks — Operation IDs](/docs/building/by-layer/L3/webhooks#operation-ids-and-url-templates)). Buyers SHOULD generate a unique value per task invocation (UUID recommended). This field is the canonical registration channel for `operation_id`; buyers MAY additionally embed routing values in the URL path or query as an aid for their own HTTP server, but the URL is opaque to the seller and the wire-level source of truth is this field. Sellers MUST NOT parse the URL to recover `operation_id`. For 3.x schema compatibility the member remains optional, but a seller MUST reject a task that registers an AdCP webhook without it using `INVALID_REQUEST`; otherwise the required webhook envelope cannot be emitted.",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^[A-Za-z0-9_.:-]{1,255}$"
    },
    "token": {
      "type": "string",
      "description": "Optional client-provided token for webhook validation. The seller MUST echo this value verbatim in every webhook payload's `token` field (see [`mcp-webhook-payload.json`](/schemas/core/mcp-webhook-payload.json) for the receiver-side validation obligation). Length bounds give receivers a defensive range check on the echoed value; senders SHOULD generate tokens with at least 128 bits of entropy (≥22 base64url characters). This is a complementary authenticity mechanism that can layer on top of the RFC 9421 webhook signature — unlike the `authentication` block below, it is not on the 4.0 removal track. Receivers that registered both a signing key (RFC 9421) and a `token` MUST NOT treat a valid token echo as authorization to skip signature verification; both checks remain independent obligations.",
      "minLength": 16,
      "maxLength": 4096
    },
    "authentication": {
      "type": "object",
      "deprecated": true,
      "x-removed-in": "4.0.0",
      "description": "Legacy authentication configuration (A2A-compatible), deprecated; removed in AdCP 4.0. Sending this block asks the seller for Bearer or HMAC-SHA256 instead of RFC 9421 signing; omitting it selects the profile that `signing_profile` names. A seller whose `webhook_signing.legacy_hmac_fallback` is `false` rejects the registration with `UNSUPPORTED_FEATURE` and `error.field` pointing at it. A seller MUST NOT sign the same webhook both ways, and a buyer MUST NOT try 9421 first and fall back to HMAC. The seller's 9421 webhook key is published at its brand.json `agents[]` `jwks_uri` with `adcp_use: \"request-signing\"` (deprecated `webhook-signing` keys stay accepted during the compatibility window). See docs/building/by-layer/L1/security.mdx#webhook-callbacks, including the `webhook_mode_mismatch` rejection buyers apply on a mode mismatch.",
      "properties": {
        "schemes": {
          "type": "array",
          "description": "Array of authentication schemes. Supported: ['Bearer'] for simple token auth, ['HMAC-SHA256'] for legacy shared-secret signing. Both are deprecated; new integrations SHOULD omit `authentication` and use the RFC 9421 webhook profile.",
          "items": {
            "$ref": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/enums/auth-scheme.json"
          },
          "minItems": 1,
          "maxItems": 1
        },
        "credentials": {
          "type": "string",
          "description": "Credentials for the legacy scheme. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.",
          "minLength": 32,
          "x-max-length-4.0": 512
        }
      },
      "required": [
        "schemes",
        "credentials"
      ],
      "additionalProperties": false
    },
    "signing_profile": {
      "x-status": "experimental",
      "$ref": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/enums/webhook-signing-profile.json",
      "description": "Webhook-signing profile the seller uses for every delivery to this `url`. When it is omitted, the seller signs with `adcp/webhook-signing/v1`, or with the legacy scheme when `authentication` is present. A seller whose `webhook_signing.profiles` does not list it rejects the registration with `INVALID_REQUEST`. MUST NOT be combined with `authentication`.",
      "x-added-in": "3.3.0"
    }
  },
  "required": [
    "url"
  ],
  "not": {
    "required": [
      "authentication",
      "signing_profile"
    ]
  }
}
