{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/signals/get-signals-request.json",
  "title": "Get Signals Request",
  "description": "Request parameters for discovering and refining signals. Use signal_spec for natural language discovery, signal_refs for exact lookups, both together to refine previous results, or discovery_mode: 'wholesale' to enumerate the agent's full priced signals feed (symmetric with get_products buying_mode: 'wholesale'). The legacy signal_ids field is deprecated.",
  "type": "object",
  "allOf": [
    {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/version-envelope.json"
    },
    {
      "description": "Conditional wholesale feed version requests are only valid for wholesale signals feed reads.",
      "if": {
        "anyOf": [
          {
            "required": [
              "if_wholesale_feed_version"
            ]
          },
          {
            "required": [
              "if_pricing_version"
            ]
          }
        ]
      },
      "then": {
        "properties": {
          "discovery_mode": {
            "const": "wholesale"
          }
        },
        "required": [
          "discovery_mode"
        ]
      }
    }
  ],
  "properties": {
    "discovery_mode": {
      "type": "string",
      "enum": [
        "brief",
        "wholesale"
      ],
      "default": "brief",
      "description": "Declares caller intent for this request. 'brief' (default): semantic discovery — signal_spec, signal_refs, or legacy signal_ids is required and the agent performs inference/RAG. 'wholesale': raw wholesale signals feed enumeration — signal_spec, signal_refs, and signal_ids MUST NOT be provided and the agent returns its full priced signals feed, paginated, scoped by filters/account/destinations/countries when present. Sellers receiving requests from pre-v3.1 clients without discovery_mode MUST default to 'brief'. Timing semantics: 'wholesale' is a wholesale signals feed read — agents SHOULD respond synchronously and MUST NOT route a 'wholesale' request through the async/Submitted arm; partial completion is signalled via the response's incomplete[] field, not via a task-handoff envelope. Agents that do not implement wholesale enumeration MAY return INVALID_REQUEST for wholesale calls; callers SHOULD probe via get_adcp_capabilities (signals.discovery_modes) first."
    },
    "account": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/account-ref.json",
      "description": "Account for this request. When provided, the signals agent returns per-account pricing options if configured. In 'wholesale' mode, this is the rate-card scope: when omitted in wholesale mode, agents return their default rate-card pricing or omit pricing_options entirely."
    },
    "signal_spec": {
      "type": "string",
      "description": "Natural language description of the desired signals. When used alone, enables semantic discovery. When combined with signal_refs, provides context for the agent but signal_ref matches are returned first. MUST NOT be provided when discovery_mode is 'wholesale'."
    },
    "signal_refs": {
      "type": "array",
      "description": "Specific signals to look up by reference. Returns exact matches for the requested SignalRef values. When combined with signal_spec, these signals anchor the starting set and signal_spec guides adjustments. MUST NOT be provided when discovery_mode is 'wholesale'.",
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/signal-ref.json"
      },
      "minItems": 1
    },
    "signal_ids": {
      "type": "array",
      "description": "DEPRECATED. Use signal_refs instead. Legacy exact lookup field using SignalId objects. MUST NOT be provided when discovery_mode is 'wholesale'.",
      "deprecated": true,
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/signal-id.json"
      },
      "minItems": 1
    },
    "destinations": {
      "type": "array",
      "description": "Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. If the authenticated caller matches one of these destinations, activation keys will be included in the response.",
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/destination.json"
      },
      "minItems": 1
    },
    "countries": {
      "type": "array",
      "description": "Countries where signals will be used (ISO 3166-1 alpha-2 codes). When omitted, no geographic filter is applied.",
      "items": {
        "type": "string",
        "pattern": "^[A-Z]{2}$"
      },
      "minItems": 1
    },
    "filters": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/signal-filters.json"
    },
    "fields": {
      "type": "array",
      "description": "Specific signal fields to include in the response, aligned with get_products.fields. Required identity and activation fields such as signal_ref or signal_id, signal_agent_segment_id, name, description, signal_type, coverage_percentage, and deployments are always included when required by the response schema. Use for progressive disclosure of rich signal-definition metadata: request fields such as demographic_predicate, taxonomy, data_sources, methodology, segmentation_criteria, criteria_url, refresh_cadence, lookback_window, onboarder, modeling, audience_expansion, device_expansion, countries, consent_basis, restricted_attributes, policy_categories, art9_basis, data_subject_rights, and last_updated when the buyer needs them inline. Omit for the agent's default discovery projection. Agents SHOULD honor requested fields for exact lookup, refinement, small custom-signal result sets, and private/source-native signals when available. fields is a projection request, not an entitlement grant; agents MAY redact requested definition fields unless the caller is authorized for the underlying lineage, methodology, and rights-routing metadata. When demographic_predicate, consent_basis, or art9_basis is projected for another provider's signal, the value remains provider-declared signal-definition posture; sellers and federating agents MUST NOT substitute their own semantics or processing basis. For broad discovery and wholesale pages, agents MAY return compact pointers instead of inlining large resources, especially when provider-published definitions can be resolved from signal_ref, taxonomy.ref, criteria_url, disclosure_url, and validators such as resolved URL plus catalog_etag, HTTP ETag/Last-Modified, or taxonomy.etag.",
      "items": {
        "type": "string",
        "enum": [
          "signal_ref",
          "signal_id",
          "signal_agent_segment_id",
          "name",
          "description",
          "value_type",
          "categories",
          "range",
          "demographic_predicate",
          "signal_type",
          "data_provider",
          "coverage_percentage",
          "deployments",
          "pricing_options",
          "taxonomy",
          "data_sources",
          "methodology",
          "segmentation_criteria",
          "criteria_url",
          "refresh_cadence",
          "lookback_window",
          "onboarder",
          "modeling",
          "audience_expansion",
          "device_expansion",
          "countries",
          "consent_basis",
          "restricted_attributes",
          "policy_categories",
          "art9_basis",
          "data_subject_rights",
          "last_updated"
        ]
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "max_results": {
      "type": "integer",
      "description": "DEPRECATED: Use pagination.max_results instead. When both fields are present, agents MUST honor pagination.max_results. When only this field is present without a pagination envelope, agents SHOULD treat it as the page size subject to a maximum of 100 results. This field will be removed in AdCP 4.0.",
      "deprecated": true,
      "minimum": 1
    },
    "pagination": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/pagination-request.json",
      "description": "Pagination parameters. Use pagination.max_results (max: 100, default: 50) and pagination.cursor for cursor-based page walks. When the deprecated top-level max_results field is also present, pagination.max_results takes precedence."
    },
    "push_notification_config": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/push-notification-config.json",
      "description": "Optional webhook configuration for async terminal completion/failure notifications on semantic signal discovery. Meaningful only for `discovery_mode: \"brief\"` requests that enter the async lifecycle. Submitted envelopes with `task_id` remain pollable through `get_task_status` (legacy `tasks/get`) whether or not this field is present. If a brief request includes this field and the agent returns a Submitted envelope, the agent MUST deliver at least the terminal completion/failure notification to the configured URL; intermediate progress notifications are MAY. If the agent cannot honor the webhook channel, it MUST reject the request with a structured error instead of silently accepting. This field does not change wholesale timing semantics: agents MUST NOT route `discovery_mode: \"wholesale\"` requests through the async/Submitted arm or emit async delivery solely because `push_notification_config` is present; partial wholesale completion is reported via `incomplete[]`."
    },
    "if_wholesale_feed_version": {
      "type": "string",
      "description": "Opaque wholesale_feed_version token returned by a prior wholesale-mode get_signals response from this agent. Only valid when discovery_mode is wholesale. When provided, the agent compares against its current wholesale signals feed version for the caller's cache_scope and MAY return an unchanged: true response (with signals omitted) if nothing has changed. The token is scope-keyed: callers cache `(cache_scope, wholesale_feed_version)` pairs. Scoping dimensions: (agent, discovery_mode, filters, destinations, countries) for cache_scope: 'public'; that tuple plus account_id for cache_scope: 'account'. pagination.cursor is NOT part of the scoping tuple. See specs/wholesale-feed-webhooks.md for the full sync pattern."
    },
    "if_pricing_version": {
      "type": "string",
      "description": "Opaque pricing_version token from a prior get_signals response. MUST only be sent together with if_wholesale_feed_version — pricing version has no structural baseline to compare against on its own. Evaluation order: (1) if_wholesale_feed_version mismatch → agent returns the full payload; (2) if_wholesale_feed_version matches but if_pricing_version mismatches → agent returns the full payload so the caller sees updated pricing_options; (3) both match → agent MAY return unchanged: true. Agents that don't track pricing separately ignore this and fall back to if_wholesale_feed_version semantics."
    },
    "context": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/context.json"
    },
    "ext": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.0/core/ext.json"
    }
  },
  "if": {
    "properties": {
      "discovery_mode": {
        "const": "wholesale"
      }
    },
    "required": [
      "discovery_mode"
    ]
  },
  "then": {
    "description": "Wholesale enumeration: signal_spec, signal_refs, and legacy signal_ids MUST NOT be present.",
    "not": {
      "anyOf": [
        {
          "required": [
            "signal_spec"
          ]
        },
        {
          "required": [
            "signal_refs"
          ]
        },
        {
          "required": [
            "signal_ids"
          ]
        }
      ]
    }
  },
  "else": {
    "description": "Brief mode (default): signal_spec, signal_refs, or legacy signal_ids is required.",
    "anyOf": [
      {
        "required": [
          "signal_spec"
        ]
      },
      {
        "required": [
          "signal_refs"
        ]
      },
      {
        "required": [
          "signal_ids"
        ]
      }
    ]
  },
  "dependencies": {
    "if_pricing_version": [
      "if_wholesale_feed_version"
    ]
  },
  "additionalProperties": true
}
