{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://adcontextprotocol.org/schemas/3.2.0-rc.3/core/reporting-status-issue.json",
  "title": "Reporting Status Issue",
  "x-status": "experimental",
  "description": "Structured reporting condition that explains delayed or action_required health without exposing credentials, provider response bodies, or internal stack traces.",
  "type": "object",
  "definitions": {
    "ReportingStatusSeverity": {
      "title": "Reporting Status Severity",
      "x-adcp-model-context-inline": true,
      "type": "string",
      "enum": [
        "delayed",
        "action_required"
      ]
    }
  },
  "properties": {
    "issue_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^[A-Za-z0-9_.:-]{1,255}$",
      "description": "Seller-issued stable identifier for this logical issue: re-emissions and later polls of the same unresolved condition reuse it, and resolution retires it, so consumers can project AdCP reporting issues into durable work items. A recurrence after resolution receives a new id."
    },
    "code": {
      "type": "string",
      "enum": [
        "REPORT_OVERDUE",
        "PRODUCTION_FAILED",
        "DELIVERY_FAILED",
        "ACCESS_REQUIRED",
        "CONFIGURATION_REQUIRED",
        "REPORTING_COVERAGE_INCOMPLETE",
        "RESOURCE_EXPIRED",
        "READER_INCOMPATIBLE",
        "HISTORY_UNAVAILABLE",
        "RECEIPT_REQUIRED",
        "RECEIPT_REJECTED",
        "ADJUSTMENT_RECEIPT_REQUIRED",
        "ADJUSTMENT_RECEIPT_REJECTED",
        "CONSUMER_STATUS_MISMATCH"
      ]
    },
    "severity": {
      "$ref": "#/definitions/ReportingStatusSeverity"
    },
    "opened_at": {
      "type": "string",
      "format": "date-time",
      "description": "When the seller first observed this logical condition, carried unchanged across every re-emission until the issue is retired. It anchors the escalation clock advertised as consumer_mismatch_escalation_seconds and lets a consumer age an issue without keeping its own first-seen table. Required when code is CONSUMER_STATUS_MISMATCH."
    },
    "issue_state": {
      "type": "string",
      "enum": [
        "open",
        "acknowledged",
        "resolved",
        "waived"
      ],
      "description": "Optional seller-maintained lifecycle for this issue_id. open is the default when omitted. acknowledged means a human on responsible_party has taken it up but the condition persists. resolved means the underlying condition no longer holds; a recurrence uses a new issue_id. waived means the parties agreed off-protocol to stop acting on it. Only open and acknowledged issues appear in issues[]; retiring an issue removes it from the projection rather than publishing it at resolved or waived, so a reader that treats a nonempty issues[] as degradation stays correct. Retiring is never a way to discharge a condition that still holds: for CONSUMER_STATUS_MISMATCH see the consumer_mismatch_lifecycle rule."
    },
    "external_ref": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "pattern": "^[A-Za-z0-9_.:-]{1,128}$",
      "description": "Optional opaque, non-secret correlation string for the party's own tracker — a ticket key, incident ID, or case number. Untrusted display text only. The character class excludes whitespace and the solidus, so the value cannot express a URL or a sentence; receivers compare, store, and display it as inert text and never dereference, resolve, or execute it. It confers no authorization and MUST NOT be used to look up state across accounts or callers."
    },
    "responsible_party": {
      "type": "string",
      "enum": [
        "buyer",
        "seller",
        "provider"
      ]
    },
    "recommended_action": {
      "type": "string",
      "enum": [
        "wait_for_retry",
        "contact_buyer",
        "contact_seller",
        "contact_provider",
        "repair_access",
        "update_configuration",
        "change_reporting_scope",
        "use_supported_reader"
      ]
    },
    "message": {
      "type": "string",
      "maxLength": 500,
      "description": "Untrusted display text only. SDKs and agents dispatch exclusively on closed code/recommended_action values and never execute embedded links or instructions."
    },
    "reporting_obligation_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^[A-Za-z0-9_.:-]{1,255}$",
      "x-entity": "reporting_obligation"
    },
    "reporting_status_id": {
      "type": "string",
      "minLength": 16,
      "maxLength": 255,
      "pattern": "^[A-Za-z0-9_.:-]{16,255}$",
      "x-entity": "reporting_consumer_status",
      "description": "Current authenticated consumer status statement that caused this mismatch."
    },
    "delivery_config_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^[A-Za-z0-9_.:-]{1,64}$",
      "x-entity": "reporting_delivery_config"
    },
    "delivery_config_version": {
      "type": "integer",
      "minimum": 1
    },
    "feed_purpose": {
      "$ref": "https://adcontextprotocol.org/schemas/3.2.0-rc.3/core/reporting-delivery-offering.json#/definitions/ReportingFeedPurpose"
    },
    "media_buy_ids": {
      "type": "array",
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.2.0-rc.3/core/reporting-coverage.json#/definitions/ReportingMediaBuyId"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "package_ids": {
      "type": "array",
      "items": {
        "$ref": "https://adcontextprotocol.org/schemas/3.2.0-rc.3/core/reporting-coverage.json#/definitions/ReportingPackageId"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "period_start": {
      "type": "string",
      "format": "date-time"
    },
    "period_end": {
      "type": "string",
      "format": "date-time"
    },
    "expected_at": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "issue_id",
    "code",
    "severity",
    "responsible_party",
    "recommended_action"
  ],
  "allOf": [
    {
      "if": {
        "properties": {
          "code": {
            "const": "CONSUMER_STATUS_MISMATCH"
          }
        },
        "required": [
          "code"
        ]
      },
      "then": {
        "required": [
          "opened_at",
          "reporting_status_id"
        ]
      }
    }
  ],
  "x-adcp-validation": {
    "issue_lifecycle": "issue_id identifies one logical condition. opened_at is fixed at first emission and MUST NOT advance while the same issue_id is re-emitted, even across ledger snapshots, health transitions, or a severity change from delayed to action_required. issue_state, when supported, moves only forward through open, acknowledged, and then resolved or waived; a recurrence after retirement receives a new issue_id and a new opened_at. external_ref is inert correlation text with no protocol meaning; a seller MUST NOT reuse one external_ref across callers on a caller-scoped issue, because that would leak the blast radius of a seller-side incident between tenants.",
    "consumer_mismatch_lifecycle": "A seller MUST NOT retire a CONSUMER_STATUS_MISMATCH out of a degraded projection while the consumer statement that caused it is still that consumer's current unsuperseded leaf. resolved is reachable only when the consumer supersedes that statement with one that agrees with the seller's projection, or the seller's own projection changes so the two no longer conflict. waived records an off-protocol agreement to stop acting and MUST NOT by itself return the period to healthy or complete. Otherwise a seller could unilaterally erase a buyer-attributed disagreement, which is the one outcome this separately attributed loop exists to prevent. A seller MAY, however, waive an uncorroborated consumer statement it has diagnosed as buyer-side; that is an explicit, readable act, not silence.",
    "consumer_mismatch_escalation": "A CONSUMER_STATUS_MISMATCH issue MUST carry opened_at and the reporting_status_id that caused it. When the seller advertises consumer_mismatch_escalation_seconds, an open or acknowledged issue still unresolved at opened_at plus that window MUST be emitted at severity action_required with a recommended_action in the contact_ family — contact_buyer, contact_seller, or contact_provider — naming the diagnosed responsible_party, and the seller MUST advertise operations_contact so the named party is reachable. wait_for_retry MUST NOT survive the escalation boundary: an unattended mismatch is an escalation, not a retry. This escalation takes precedence over the stale-received grace window in get-reporting-status-response.json: when the two windows overlap, the escalation boundary wins and the issue is action_required even if the grace window has not closed. An issue already retired under consumer_mismatch_lifecycle is not subject to this rule, because it is no longer in issues[]."
  },
  "additionalProperties": false
}
