{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "/schemas/3.1.0-rc.4/compliance/comply-test-controller-request.json",
  "title": "Comply Test Controller Request",
  "description": "Request payload for the comply_test_controller tool. Triggers seller-side state transitions for compliance testing. Sandbox only — sellers MUST NOT expose this tool in production. Naturally idempotent: each known `scenario` is either a lookup (`list_scenarios`) or a state-forcing operation whose target state is carried in the payload (`force_*_status`, `simulate_*`), so replays converge to the same observable state without needing an idempotency_key. The compliance harness drives this tool deterministically and does not rely on the seller's at-most-once replay cache.",
  "type": "object",
  "allOf": [
    {
      "$ref": "/schemas/3.1.0-rc.4/core/version-envelope.json"
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_creative_status"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "creative_id",
              "status"
            ],
            "properties": {
              "status": {
                "$ref": "/schemas/3.1.0-rc.4/enums/creative-status.json"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_creative_purge"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "creative_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_account_status"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "account_id",
              "status"
            ],
            "properties": {
              "status": {
                "$ref": "/schemas/3.1.0-rc.4/enums/account-status.json"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_media_buy_status"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "media_buy_id",
              "status"
            ],
            "properties": {
              "status": {
                "$ref": "/schemas/3.1.0-rc.4/enums/media-buy-status.json"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_create_media_buy_arm"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "arm"
            ],
            "allOf": [
              {
                "if": {
                  "properties": {
                    "arm": {
                      "const": "submitted"
                    }
                  }
                },
                "then": {
                  "required": [
                    "task_id"
                  ]
                }
              }
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_task_completion"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "task_id",
              "result"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_session_status"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "session_id",
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "complete",
                  "terminated"
                ]
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "simulate_delivery"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "media_buy_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "simulate_budget_spend"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "spend_percentage"
            ],
            "anyOf": [
              {
                "required": [
                  "account_id"
                ]
              },
              {
                "required": [
                  "media_buy_id"
                ]
              }
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_product"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "product_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_pricing_option"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "product_id",
              "pricing_option_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_creative"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "creative_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_plan"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "plan_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_media_buy"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "media_buy_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_creative_format"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "format_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "seed_measurement_catalog"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "vendor",
              "metrics"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "query_upstream_traffic"
          }
        }
      },
      "then": {
        "properties": {
          "params": {
            "type": "object",
            "properties": {
              "since_timestamp": {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp; only return calls recorded at or after this time. Default: session start. Used by storyboard runners to scope upstream_traffic assertions to a specific step's window."
              },
              "endpoint_pattern": {
                "type": "string",
                "description": "Optional server-side filter; reduces response size. Wildcard pattern matched against `<METHOD> <URL>`, e.g. 'POST */audience/upload' or 'POST *'. When omitted, all recorded calls in the window are returned. **Grammar (normative):** `*` matches zero or more characters of any kind including `/`. No other characters have wildcard semantics — `?` is a literal question mark, `[` and `]` are literal brackets, etc. There is no escape mechanism — `*` is always a wildcard; if literal-asterisk matching is required, callers omit `endpoint_pattern` and filter response-side. Implementations MUST anchor the pattern (full-string match, not substring search). This narrow grammar is intentional — `endpoint_pattern` exists to discriminate platform endpoints (e.g., `POST */audience/upload` vs `POST */events/log`), not to express full path-segment grammars. Cross-runner determinism depends on this pinning: a runner that ships POSIX-glob semantics (`*` per-segment, `?` single-char-any) would grade the same storyboard differently than a runner picking the more permissive single-wildcard reading."
              },
              "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1000,
                "default": 100,
                "description": "Maximum number of calls to return. The response carries `total_count` and `truncated` so the runner can detect overflow."
              },
              "attestation_mode": {
                "type": "string",
                "enum": [
                  "raw",
                  "digest"
                ],
                "default": "raw",
                "description": "Requested response shape per recorded call. `raw` (default) returns full payload — the load-bearing assertion target for arbitrary `payload_must_contain` paths, and the existing v2.0.0 contract. `digest` returns only `payload_digest_sha256` + `payload_length` + `content_type` + optional `identifier_match_proofs[]`, so privacy-conscious adopters can support `upstream_traffic` conformance without raw-payload disclosure. Whether digest mode satisfies a given adopter's data-handling obligations (GDPR processor responsibilities, internal data-classification policy, contractual restrictions) is for that adopter's counsel to determine — the spec doesn't promise digest mode clears any specific legal bar. Synthetic-vectors-only still applies (see UpstreamTrafficSuccess top-level description in comply-test-controller-response.json): digest mode reduces what the runner sees, but the controller must still operate on synthetic test data, not production traffic — running queries against production payloads would let a runner with a precomputed digest set learn membership of arbitrary identifiers in the adopter's user base. Adopters MAY unilaterally downgrade a `raw` request to `digest` when their policy requires it; the response's per-call `attestation_mode` field echoes what was actually returned. Storyboards that strictly require raw introspection set `attestation_mode_required: \"raw\"` on their `upstream_traffic` check — calls returned in digest mode then grade not_applicable rather than failing."
              },
              "identifier_value_digests": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[a-f0-9]{64}$"
                },
                "maxItems": 64,
                "description": "When `attestation_mode` is `digest`, the runner MAY supply SHA-256 digests (lowercase hex, 64 chars) of identifier values it wants the controller to verify echo for. The controller scans each recorded call's payload for string tokens whose SHA-256 hash matches any digest in this list and returns booleans in `recorded_calls[].identifier_match_proofs[]`. Lets the runner verify `identifier_paths` echo without ever transmitting plaintext identifiers to the controller — the digest is the comparison key. Cap of 64 digests per query to bound the controller's work; storyboards with more identifiers SHOULD batch across multiple queries. Has no effect when `attestation_mode` is `raw` (the runner does the matching in-process against the full payload)."
              }
            },
            "additionalProperties": true
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "query_provenance_audit_observations"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "creative_id"
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "scenario": {
            "const": "force_upstream_unavailable"
          }
        }
      },
      "then": {
        "required": [
          "params"
        ],
        "properties": {
          "params": {
            "required": [
              "tool"
            ]
          }
        }
      }
    }
  ],
  "x-mutates-state": true,
  "properties": {
    "scenario": {
      "type": "string",
      "description": "Test scenario to execute. 'list_scenarios' discovers supported scenarios. 'force_*' and 'simulate_*' trigger state transitions. 'force_creative_purge' destroys or tombstones a sandbox creative so account-level `creative.purged` webhooks can be observed where the seller supports the lifecycle surface. 'seed_*' scenarios pre-populate fixtures (product, pricing option, creative, plan, media buy, creative format, measurement catalog) so storyboards can reference fixture IDs and external-catalog facts without implementers guessing which fixtures the conformance suite expects. 'query_upstream_traffic' returns outbound HTTP calls the agent has made since session start (or since a caller-supplied timestamp), so storyboard runners can assert upstream side-effects via `check: upstream_traffic`. 'query_provenance_audit_observations' returns sandbox-only audit observations recorded for a submitted creative so storyboards can assert non-blocking governance observations without exposing an internal audit log on public seller responses. 'force_upstream_unavailable' marks a named upstream dependency as unreachable for the duration of the compliance session (or until the seller resets it), so storyboards can exercise stale-cache fallback paths — see the `stale_response_advisory` universal storyboard. The contract raises the bar against unintentional façades — adapters that satisfy AdCP schema requirements with synthetic placeholders. It is NOT an adversarial integrity check: adopters self-report their own traffic. Adopters MUST scope the response to traffic caused by the requesting principal's session/auth context — cross-caller traffic MUST NOT be returned, regardless of the supplied since_timestamp. Multi-tenant sandboxes MUST key the recording buffer on the comply_test_controller invocation's auth principal. Runners and sellers MUST accept unknown scenario strings — new scenarios may be added in additive releases."
    },
    "params": {
      "type": "object",
      "description": "Scenario-specific parameters. Required for all scenarios except list_scenarios.",
      "properties": {
        "creative_id": {
          "type": "string",
          "description": "Creative to transition (force_creative_status) or seed (seed_creative)."
        },
        "account_id": {
          "type": "string",
          "description": "Account to transition. Used by force_account_status and simulate_budget_spend."
        },
        "media_buy_id": {
          "type": "string",
          "description": "Media buy to transition (force_media_buy_status, simulate_delivery, simulate_budget_spend) or seed (seed_media_buy)."
        },
        "session_id": {
          "type": "string",
          "description": "Session to transition. Used by force_session_status."
        },
        "product_id": {
          "type": "string",
          "description": "Product to seed. Used by seed_product and seed_pricing_option."
        },
        "pricing_option_id": {
          "type": "string",
          "description": "Pricing option to seed, scoped to a product. Used by seed_pricing_option."
        },
        "plan_id": {
          "type": "string",
          "description": "Plan to seed. Used by seed_plan."
        },
        "fixture": {
          "type": "object",
          "description": "Arbitrary fixture payload carried by seed_* scenarios. Shape matches the domain object the seed scenario creates (product, creative, plan, media buy, pricing option). Seller MAY reject malformed fixtures with INVALID_PARAMS. Kept permissive so storyboard authors can declare the minimum shape each test needs without the spec locking down every field.",
          "additionalProperties": true
        },
        "status": {
          "type": "string",
          "description": "Target status for the resource. Type depends on scenario: creative-status for force_creative_status, account-status for force_account_status, media-buy-status for force_media_buy_status. For force_session_status, must be 'complete' or 'terminated'."
        },
        "rejection_reason": {
          "type": "string",
          "description": "Reason for rejection. Used by force_creative_status and force_media_buy_status when status = rejected."
        },
        "purge_kind": {
          "type": "string",
          "enum": [
            "soft",
            "hard"
          ],
          "description": "Purge mode for force_creative_purge. soft retains a list_creatives tombstone; hard removes the creative entirely."
        },
        "reason_code": {
          "$ref": "/schemas/3.1.0-rc.4/enums/creative-event-reason-code.json",
          "description": "Creative lifecycle reason code for force_creative_purge."
        },
        "reason_detail": {
          "type": "string",
          "description": "Human-readable detail for force_creative_purge."
        },
        "termination_reason": {
          "type": "string",
          "description": "Reason for termination (e.g., session_timeout, host_terminated, policy_violation). Used by force_session_status when status = terminated."
        },
        "impressions": {
          "type": "integer",
          "minimum": 0,
          "description": "Impressions to simulate. Used by simulate_delivery."
        },
        "clicks": {
          "type": "integer",
          "minimum": 0,
          "description": "Clicks to simulate. Used by simulate_delivery."
        },
        "conversions": {
          "type": "integer",
          "minimum": 0,
          "description": "Conversions to simulate. Used by simulate_delivery."
        },
        "reported_spend": {
          "type": "object",
          "description": "Spend as reported in delivery data. Does not affect budget. Used by simulate_delivery.",
          "properties": {
            "amount": {
              "type": "number",
              "minimum": 0
            },
            "currency": {
              "type": "string",
              "pattern": "^[A-Z]{3}$"
            }
          },
          "required": [
            "amount",
            "currency"
          ]
        },
        "reach": {
          "type": "number",
          "minimum": 0,
          "description": "Unique reach count to inject into the simulated delivery row. Used by simulate_delivery. The unit of measurement matches the reach_unit declared on the media buy's optimization goal. When supplied, sellers MUST surface this value at `totals.reach` in the next get_media_buy_delivery response."
        },
        "frequency": {
          "type": "number",
          "minimum": 0,
          "description": "Average frequency per reach unit to inject. Used by simulate_delivery. When supplied alongside reach, sellers MUST surface this value at `totals.frequency`. The measurement window for this frequency value is declared in `reach_window` when also present."
        },
        "reach_window": {
          "type": "object",
          "description": "Measurement window semantics to attach to the simulated reach and frequency values. Used by simulate_delivery. When present, sellers MUST populate `reach_window` on the delivery row so buyers can determine the window semantics (cumulative vs. period vs. rolling). Omitting this param produces a row with no `reach_window` — valid per schema but sum-unsafe. Mirrors the `reach_window` shape in delivery-metrics.json.",
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "cumulative",
                "period",
                "rolling"
              ],
              "description": "Window kind. `cumulative` — no period field required. `period` and `rolling` — period field REQUIRED."
            },
            "period": {
              "allOf": [
                {
                  "$ref": "/schemas/3.1.0-rc.4/core/duration.json"
                }
              ],
              "description": "Duration of the measurement window. REQUIRED when kind is `period` or `rolling`. Matches the `period` field in delivery-metrics.json's reach_window block."
            }
          },
          "required": [
            "kind"
          ],
          "allOf": [
            {
              "if": {
                "properties": {
                  "kind": {
                    "enum": [
                      "period",
                      "rolling"
                    ]
                  }
                },
                "required": [
                  "kind"
                ]
              },
              "then": {
                "required": [
                  "period"
                ]
              }
            }
          ],
          "additionalProperties": true
        },
        "viewability": {
          "type": "object",
          "description": "Viewability metrics to inject into the simulated delivery row. Used by simulate_delivery. When present, sellers MUST surface these values inside `viewability` on the next get_media_buy_delivery response. Mirrors the `viewability` block in delivery-metrics.json: measurable_impressions is the shared denominator for viewable_rate and viewed_seconds. Sellers SHOULD include standard whenever measured values are present.",
          "properties": {
            "measurable_impressions": {
              "type": "number",
              "minimum": 0,
              "description": "Impressions where viewability could be measured. Coverage denominator for viewable_rate and viewed_seconds."
            },
            "viewable_impressions": {
              "type": "number",
              "minimum": 0,
              "description": "Impressions that met the viewability threshold."
            },
            "viewable_rate": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "Viewable impression rate (viewable_impressions / measurable_impressions)."
            },
            "viewed_seconds": {
              "type": "number",
              "minimum": 0,
              "description": "Average in-view duration per measurable impression in seconds. Reporting counterpart to the viewed_seconds optimization metric."
            },
            "standard": {
              "$ref": "/schemas/3.1.0-rc.4/enums/viewability-standard.json",
              "description": "Viewability measurement standard governing the threshold for viewable_rate and viewed_seconds."
            }
          },
          "additionalProperties": true
        },
        "spend_percentage": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "Spend to this percentage of budget (0–100). Used by simulate_budget_spend."
        },
        "arm": {
          "type": "string",
          "enum": [
            "submitted",
            "input-required"
          ],
          "description": "Response arm for the next create_media_buy call. Used by force_create_media_buy_arm. v1 supports the two arms a buyer-supplied directive can shape without fabricating server state: 'submitted' (async task envelope) and 'input-required' (errors-branch). 'completed' is covered by seed_media_buy + a normal flow; 'working' is an out-of-band progress signal, not an initial response arm."
        },
        "task_id": {
          "type": "string",
          "maxLength": 128,
          "description": "Deterministic task handle the seller MUST emit verbatim on the next create_media_buy response when arm is 'submitted'. The seller MUST accept this exact value on subsequent tasks/get calls within the same authenticated sandbox account. Sandbox task_ids are caller-opaque strings — the seller's production task-id format rules do not apply.",
          "x-entity": "task"
        },
        "message": {
          "type": "string",
          "maxLength": 2000,
          "description": "Optional human-readable explanation surfaced on the next create_media_buy response. Used by force_create_media_buy_arm for the 'submitted' and 'working' arms. Plain text only."
        },
        "format_id": {
          "type": "string",
          "description": "Creative format ID to seed. Used by seed_creative_format. The seller MUST expose this format ID in list_creative_formats responses for the duration of the compliance session."
        },
        "vendor": {
          "$ref": "/schemas/3.1.0-rc.4/core/brand-ref.json",
          "description": "Measurement vendor whose catalog is being seeded. Used by seed_measurement_catalog. The seller MUST treat `metrics[]` as this vendor's `get_adcp_capabilities.measurement.metrics[]` snapshot for the duration of the compliance session."
        },
        "metrics": {
          "type": "array",
          "description": "Measurement catalog entries to seed for the vendor. Used by seed_measurement_catalog. Shape mirrors `get_adcp_capabilities.measurement.metrics[]`; each metric_id is unique within the seeded vendor catalog.",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "object",
            "required": [
              "metric_id"
            ],
            "properties": {
              "metric_id": {
                "$ref": "/schemas/3.1.0-rc.4/core/vendor-metric-id.json"
              }
            },
            "additionalProperties": true
          }
        },
        "tool": {
          "type": "string",
          "description": "Tool name whose upstream dependency to affect. Used by force_upstream_unavailable. The seller marks the named upstream as unreachable for subsequent calls to this tool within the compliance session. Sellers MAY accept a wildcard ('*') to affect all tools."
        },
        "upstream_name": {
          "type": "string",
          "description": "Human-readable identifier for the upstream dependency to force unavailable (e.g., 'inventory-service', 'creative-agent'). Used by force_upstream_unavailable. When omitted, the seller marks its default upstream for the specified tool as unavailable. Sellers MUST include the same name in STALE_RESPONSE error.details.upstream.name on the affected response."
        },
        "result": {
          "$ref": "/schemas/3.1.0-rc.4/core/async-response-data.json",
          "description": "Completion payload to record against the task. Used by force_task_completion. Validates against the async-response-data union — for create_media_buy this is a CreateMediaBuyResponse with media_buy_id and packages. The seller MUST deliver this verbatim to the buyer's push_notification_config.url (the canonical 3.0 path for completion payload delivery), with all caller-supplied fields preserved (sellers MAY augment with seller-controlled fields like created_at or dsp_* IDs but MUST NOT overwrite caller-supplied values). A typed projection on the polling response is tracked for 3.1 (#3123). Sellers MUST emit INVALID_PARAMS if the payload does not validate against the response branch for the task's original method, and MAY reject payloads exceeding 256 KB with INVALID_PARAMS."
        }
      },
      "additionalProperties": true
    },
    "context": {
      "$ref": "/schemas/3.1.0-rc.4/core/context.json"
    },
    "ext": {
      "$ref": "/schemas/3.1.0-rc.4/core/ext.json"
    },
    "account": {
      "type": "object",
      "description": "Sandbox account assertion. The runner MUST set sandbox: true on every comply_test_controller request. The seller MUST refuse the request (returning a structured error) if the targeted account is not a sandbox account in the seller's persisted records. This field is a caller-side declaration of intent — it does not grant sandbox status; sellers verify against their own account state. The (Sandbox) verification tier is defined by this gate: real production endpoints accept sandbox-flagged traffic and process it without real-world side effects, no separate test-mode endpoint required. See spec issue #3755 and the (Sandbox) framing in #4379.",
      "required": [
        "sandbox"
      ],
      "properties": {
        "sandbox": {
          "type": "boolean",
          "const": true,
          "description": "MUST be true. The seller MUST verify the targeted account is sandbox by looking up the persisted account record, not by trusting this field. A request asserting sandbox: false schema-rejects before reaching the seller — defense-in-depth on top of the per-request gate."
        }
      },
      "additionalProperties": true
    }
  },
  "required": [
    "scenario",
    "account"
  ],
  "additionalProperties": true,
  "examples": [
    {
      "description": "List supported scenarios",
      "data": {
        "scenario": "list_scenarios",
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Force a creative to rejected status",
      "data": {
        "scenario": "force_creative_status",
        "params": {
          "creative_id": "cr-123",
          "status": "rejected",
          "rejection_reason": "Brand safety policy violation"
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Force account suspension",
      "data": {
        "scenario": "force_account_status",
        "params": {
          "account_id": "acct-456",
          "status": "suspended"
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Force the next create_media_buy call into the submitted arm with a deterministic task_id",
      "data": {
        "scenario": "force_create_media_buy_arm",
        "params": {
          "arm": "submitted",
          "task_id": "task_async_signed_io_q2",
          "message": "Awaiting IO signature from sales team; typical turnaround 2–4 hours"
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Force a previously-submitted create_media_buy task to completion with a result payload",
      "data": {
        "scenario": "force_task_completion",
        "params": {
          "task_id": "task_async_signed_io_q2",
          "result": {
            "media_buy_id": "mb_async_signed_io_q2",
            "status": "active",
            "packages": [
              {
                "package_id": "pkg-0",
                "product_id": "async_signed_io_q2",
                "budget": 30000
              }
            ]
          }
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Simulate session timeout",
      "data": {
        "scenario": "force_session_status",
        "params": {
          "session_id": "sess-abc",
          "status": "terminated",
          "termination_reason": "session_timeout"
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Simulate delivery data",
      "data": {
        "scenario": "simulate_delivery",
        "params": {
          "media_buy_id": "mb-789",
          "impressions": 10000,
          "clicks": 150,
          "reported_spend": {
            "amount": 150,
            "currency": "USD"
          }
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Simulate budget consumption to 95%",
      "data": {
        "scenario": "simulate_budget_spend",
        "params": {
          "media_buy_id": "mb-789",
          "spend_percentage": 95
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Seed a product fixture so subsequent storyboard steps can reference it by ID",
      "data": {
        "scenario": "seed_product",
        "params": {
          "product_id": "test-product",
          "fixture": {
            "delivery_type": "non_guaranteed",
            "channels": [
              "display"
            ],
            "pricing_options": [
              {
                "pricing_option_id": "test-pricing",
                "pricing_model": "cpm",
                "currency": "USD",
                "floor_price": 1
              }
            ]
          }
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Seed an approved creative fixture",
      "data": {
        "scenario": "seed_creative",
        "params": {
          "creative_id": "campaign_hero_video",
          "fixture": {
            "status": "approved",
            "format_id": {
              "id": "video_30s"
            }
          }
        },
        "account": {
          "sandbox": true
        }
      }
    },
    {
      "description": "Query upstream traffic recorded since a storyboard step's request timestamp",
      "data": {
        "scenario": "query_upstream_traffic",
        "params": {
          "since_timestamp": "2026-05-02T14:30:00Z",
          "endpoint_pattern": "POST *",
          "limit": 100
        },
        "account": {
          "sandbox": true
        }
      }
    }
  ]
}
