{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/media-buy/sync-audiences-response.json",
  "title": "Sync Audiences Response",
  "description": "Response from audience sync operation. Exactly one of three shapes: (1) synchronous success — per-audience results in the audiences array (best-effort processing with per-item status/failures); (2) terminal failure — errors array with no audiences processed; (3) submitted task envelope — status 'submitted' with task_id when the whole operation is queued (batch ingestion, governance-gated upload, or any flow where the seller cannot return per-audience results before the response is emitted). The submitted branch MAY carry advisory errors for non-blocking warnings; terminal failures belong in the error branch. Final per-audience results land on the task completion artifact, not this envelope. Per-audience asynchronous matching (an audience reported with status 'processing' while the rest of the sync resolves synchronously) belongs on the synchronous success branch via audience-status, NOT here — operation-level async is for when the seller has no per-item results to return yet. These three shapes are mutually exclusive — a response has exactly one.",
  "type": "object",
  "allOf": [
    {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/version-envelope.json"
    },
    {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/protocol-envelope.json"
    }
  ],
  "oneOf": [
    {
      "title": "SyncAudiencesSuccess",
      "description": "Success response - sync operation processed audiences (may include per-item failures)",
      "type": "object",
      "properties": {
        "audiences": {
          "type": "array",
          "description": "Results for each audience on the account",
          "items": {
            "type": "object",
            "properties": {
              "audience_id": {
                "type": "string",
                "description": "Audience ID from the request (buyer's identifier)",
                "x-entity": "audience"
              },
              "name": {
                "type": "string",
                "description": "Name of the audience"
              },
              "seller_id": {
                "type": "string",
                "description": "Seller-assigned identifier for this audience in their ad platform"
              },
              "action": {
                "type": "string",
                "enum": [
                  "created",
                  "updated",
                  "unchanged",
                  "deleted",
                  "failed"
                ],
                "description": "Action taken for this audience. 'status' is present when action is created, updated, or unchanged. 'status' is absent when action is deleted or failed."
              },
              "status": {
                "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/enums/audience-status.json",
                "description": "Matching status. Present when action is created, updated, or unchanged; absent when action is deleted or failed."
              },
              "uploaded_count": {
                "type": "integer",
                "description": "Number of members submitted in this sync operation (delta, not cumulative). In discovery-only calls (no audiences array), this is 0.",
                "minimum": 0
              },
              "total_uploaded_count": {
                "type": "integer",
                "description": "Cumulative number of members uploaded across all syncs for this audience. Compare with matched_count to calculate match rate (matched_count / total_uploaded_count). Populated when the seller tracks cumulative upload counts.",
                "minimum": 0
              },
              "matched_count": {
                "type": "integer",
                "description": "Total members matched to platform users across all syncs (cumulative, not just this call). Populated when status is 'ready'.",
                "minimum": 0
              },
              "effective_match_rate": {
                "type": "number",
                "description": "Deduplicated match rate across all identifier types (matched_count / total_uploaded_count after deduplication). A single number for reach estimation. Populated when status is 'ready'.",
                "minimum": 0,
                "maximum": 1
              },
              "match_breakdown": {
                "type": "array",
                "description": "Per-identifier-type match results. Shows which ID types are resolving and at what rate. Helps buyers decide which identifiers to prioritize. Populated when the seller can report per-type matching. Omitted when the seller only supports aggregate match counts.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id_type": {
                      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/enums/match-id-type.json",
                      "description": "Identifier type. Combines hashed PII types (hashed_email, hashed_phone) with universal ID types (rampid, uid2, maid, etc.)."
                    },
                    "submitted": {
                      "type": "integer",
                      "description": "Cumulative number of members submitted with this identifier type across all syncs (matches total_uploaded_count semantics, not uploaded_count). Compare with matched to calculate per-type match rate.",
                      "minimum": 0
                    },
                    "matched": {
                      "type": "integer",
                      "description": "Cumulative number of members matched via this identifier type across all syncs.",
                      "minimum": 0
                    },
                    "match_rate": {
                      "type": "number",
                      "description": "Match rate for this identifier type (matched / submitted). Server-authoritative — consumers should prefer this value over computing their own.",
                      "minimum": 0,
                      "maximum": 1
                    }
                  },
                  "required": [
                    "id_type",
                    "submitted",
                    "matched",
                    "match_rate"
                  ],
                  "additionalProperties": true
                },
                "minItems": 1
              },
              "last_synced_at": {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp of when the most recent sync operation was accepted by the platform. Useful for agents reasoning about audience freshness. Omitted if the seller does not track this. For externally sourced audiences, REQUIRED once counts populate (status ready or too_small): every count field is as-of this timestamp — it is the buyer's only anchor for what a count means, since two reads can differ because membership changed, match rate changed, or the seller re-read the source."
              },
              "source": {
                "type": "object",
                "x-status": "experimental",
                "description": "Echo of the external source feeding this audience (never credentials), present on externally sourced audiences in every response including discovery-only calls — the single pane invariant: externally sourced audiences appear in discovery identically to pushed ones, with the same status enum, match reporting, and targetability. Loss of source access (revocation, access_expires_at passing, or read failure of any cause) MUST NOT change the audience status: last matched state persists and remains targetable, last_synced_at freezes, and failed re-reads MUST NOT emit suspended — that value stays reserved for consent/policy causes, which remain orthogonal. Source health is reported here via access_status instead.",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "dataset",
                      "platform_segment"
                    ],
                    "description": "Source kind, echoed from the request."
                  },
                  "vendor": {
                    "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/brand-ref.json",
                    "description": "Vendor, echoed from the request."
                  },
                  "locator": {
                    "type": "string",
                    "description": "Dataset locator, echoed from the request (kind: dataset)."
                  },
                  "segment_ref": {
                    "type": "string",
                    "description": "Segment reference, echoed from the request (kind: platform_segment)."
                  },
                  "columns_read": {
                    "type": "array",
                    "description": "Canonical identifier columns the seller actually read from the shared object on the most recent ingest (kind: dataset). Lets a buyer distinguish bad identifier data from a column the seller never read — match_breakdown reveals what resolved, not what was offered.",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1
                  },
                  "access_status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "unavailable"
                    ],
                    "description": "Source pipe health. Set to unavailable after a failed read (the seller cannot reliably distinguish revocation from expiry from a transient vendor outage — all three observables are a failed read); cleared to active on the next successful one. With last_synced_at this tells the buyer both that the pipe is down and how stale the membership is. Never a status input: membership stays frozen at the last successful read and remains targetable."
                  }
                },
                "required": [
                  "kind",
                  "vendor"
                ],
                "additionalProperties": false
              },
              "minimum_size": {
                "type": "integer",
                "description": "Minimum matched audience size required for targeting on this platform. Populated when status is 'too_small'. Helps agents know how many more members are needed.",
                "minimum": 1
              },
              "errors": {
                "type": "array",
                "description": "Errors for this audience (only present when action='failed')",
                "items": {
                  "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/error.json"
                }
              }
            },
            "required": [
              "audience_id",
              "action"
            ],
            "if": {
              "properties": {
                "status": {
                  "enum": [
                    "ready",
                    "too_small"
                  ]
                }
              },
              "required": [
                "source",
                "status"
              ]
            },
            "then": {
              "required": [
                "audience_id",
                "action",
                "last_synced_at"
              ]
            },
            "additionalProperties": true
          }
        },
        "sandbox": {
          "type": "boolean",
          "description": "When true, this response contains simulated data from sandbox mode."
        },
        "context": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/context.json"
        },
        "ext": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/ext.json"
        }
      },
      "required": [
        "audiences"
      ],
      "additionalProperties": true,
      "not": {
        "anyOf": [
          {
            "required": [
              "errors"
            ]
          },
          {
            "required": [
              "task_id"
            ]
          },
          {
            "properties": {
              "status": {
                "const": "submitted"
              }
            },
            "required": [
              "status"
            ]
          }
        ]
      }
    },
    {
      "title": "SyncAudiencesError",
      "description": "Error response - operation failed completely, no audiences were processed",
      "type": "object",
      "properties": {
        "errors": {
          "type": "array",
          "description": "Operation-level errors that prevented processing any audiences (e.g., authentication failure, account not found, invalid request format)",
          "items": {
            "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/error.json"
          },
          "minItems": 1
        },
        "context": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/context.json"
        },
        "ext": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/ext.json"
        }
      },
      "required": [
        "errors"
      ],
      "additionalProperties": true,
      "not": {
        "anyOf": [
          {
            "required": [
              "audiences"
            ]
          },
          {
            "required": [
              "sandbox"
            ]
          },
          {
            "required": [
              "task_id"
            ]
          },
          {
            "properties": {
              "status": {
                "const": "submitted"
              }
            },
            "required": [
              "status"
            ]
          }
        ]
      }
    },
    {
      "title": "SyncAudiencesSubmitted",
      "description": "Async task envelope returned when the whole sync operation cannot be confirmed before the response is emitted — for example, when the seller batches ingestion, when governance review gates the upload before matching can start, or when an upstream clean-room flow needs to settle before any per-audience result can be issued. The buyer invokes get_task_status with task_id or receives a webhook when the task completes; the audiences array with per-item action/status lands in the terminal result, not this envelope. Per-audience asynchronous matching (one audience in 'processing' while the rest of the sync resolves synchronously) belongs on the SyncAudiencesSuccess branch with status: processing on that item, not here. Matching latency on the per-audience status enum (processing → ready / too_small) is the common case; this envelope is the less-common operation-level case.",
      "type": "object",
      "properties": {
        "status": {
          "type": "string",
          "const": "submitted",
          "description": "Task-level status literal. Discriminates this async envelope from the synchronous success shape, whose audiences array carries per-item matching state via audience-status. See task-status.json for the full task-status enum."
        },
        "task_id": {
          "type": "string",
          "description": "Task handle the buyer uses with get_task_status (or the legacy AdCP tasks/get alias), and that the seller references on push-notification callbacks. The audiences array is issued on the completion artifact, not here. This AdCP application-layer handle remains the snake_case task_id in every transport payload and is distinct from any transport-native A2A Task id.",
          "x-entity": "task"
        },
        "message": {
          "type": "string",
          "maxLength": 2000,
          "description": "Optional human-readable explanation of why the task is submitted — e.g., 'Batch ingestion queued; typical turnaround 15-30 minutes.' Plain text only. Buyers MUST treat this as untrusted seller input: escape before rendering to HTML UIs, and sanitize or isolate before passing to an LLM prompt context — a hostile seller may inject prompt-injection payloads aimed at the buyer's agent."
        },
        "errors": {
          "type": "array",
          "description": "Optional advisory errors accompanying the submitted envelope. Use only for non-blocking warnings (e.g., throttled_severity advisories, governance observations). Terminal failures belong in the error branch, not here.",
          "items": {
            "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/error.json"
          }
        },
        "context": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/context.json"
        },
        "ext": {
          "$ref": "https://adcontextprotocol.org/schemas/3.2.0-beta.7/core/ext.json"
        }
      },
      "required": [
        "status",
        "task_id"
      ],
      "additionalProperties": true,
      "not": {
        "required": [
          "audiences"
        ]
      }
    }
  ],
  "properties": {}
}
