Scope3 Campaigns API

Manage advertising campaigns

Operations 21

GET /campaigns List campaigns #
POST /campaigns Create campaign #
PUT /campaigns/{campaignId} Update campaign #
GET /campaigns/{campaignId} Get campaign #
DELETE /campaigns/{campaignId} Delete campaign #
GET /campaigns/{campaignId}/delivery Get campaign delivery (alpha) #
POST /campaigns/{campaignId}/execute Execute campaign #
POST /campaigns/{campaignId}/pause Pause campaign #
POST /campaigns/{campaignId}/reactivate Reactivate campaign #
POST /campaigns/{campaignId}/auto-select-products Auto-select products #
GET /campaigns/{campaignId}/media-buy-status Get media buy ADCP status #
GET /media-buys/{mediaBuyId} Get media buy #
PATCH /media-buys/{mediaBuyId} Update media buy #
GET /media-buys/{mediaBuyId}/packages List media buy packages #
POST /media-buys/{mediaBuyId}/pause Pause media buy #
POST /media-buys/{mediaBuyId}/reactivate Reactivate media buy #
GET /campaigns/{campaignId}/products Get campaign products #
GET /targeting/dimensions List targeting dimensions #
GET /targeting/dimensions/{system} List targeting dimension values #
GET /targeting/dimensions/{system}/resolve Resolve targeting dimension text #
GET /targeting/geo-metros List geo metros #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/scope3-campaigns-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

scope3-campaigns-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Scope3 Buyer Campaigns API
  version: 2.0.0
  description: 'REST API for advertisers to manage advertisers, campaigns, and reporting.


    ## Authentication


    All endpoints require a Bearer token in the Authorization header:

    ```

    Authorization: Bearer your-api-key

    ```


    ## Base URL


    `https://api.interchange.io/api/v2/buyer`


    ## For AI Agents


    AI agents can use the MCP endpoint at `/mcp/v2/buyer` with three tools:

    - `initialize`: Start an MCP session

    - `api_call`: Make REST API calls

    - `ask_about_capability`: Learn about API features'
servers:
- url: https://api.interchange.io/api/v2/buyer
  description: Production server
tags:
- name: Campaigns
  description: Manage advertising campaigns
paths:
  /campaigns:
    get:
      operationId: listCampaigns
      summary: List campaigns
      description: List campaigns with optional filtering by advertiser and status.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: query
        name: advertiserId
        schema:
          description: Filter by advertiser ID
          example: '12345'
          type: string
          pattern: ^\d+$
        description: Filter by advertiser ID
      - in: query
        name: name
        schema:
          description: Filter by campaign name (case-insensitive, partial match)
          example: Summer
          type: string
        description: Filter by campaign name (case-insensitive, partial match)
      - in: query
        name: status
        schema:
          description: Filter by campaign status. Accepts a single value or repeated values; pass "ALL" to include every status. Defaults to the non-terminal statuses (ACTIVE, DRAFT, PAUSED) — every campaign that could still spend. Completed, canceled, and archived campaigns require an explicit status filter (or "ALL").
          example: ACTIVE
          anyOf:
          - type: array
            items:
              $ref: '#/components/schemas/CampaignStatusListFilter'
          - allOf:
            - $ref: '#/components/schemas/CampaignStatusListFilter'
        description: Filter by campaign status. Accepts a single value or repeated values; pass "ALL" to include every status. Defaults to the non-terminal statuses (ACTIVE, DRAFT, PAUSED) — every campaign that could still spend. Completed, canceled, and archived campaigns require an explicit status filter (or "ALL").
      - in: query
        name: mode
        schema:
          description: Filter by campaign mode. Accepts a single mode (`?mode=directed`) or repeated values (`?mode=discovery&mode=performance`).
          example: directed
          anyOf:
          - type: array
            items:
              $ref: '#/components/schemas/CampaignMode'
          - allOf:
            - $ref: '#/components/schemas/CampaignMode'
        description: Filter by campaign mode. Accepts a single mode (`?mode=directed`) or repeated values (`?mode=discovery&mode=performance`).
      - in: query
        name: management
        schema:
          description: 'Filter by management state: "tracked" (campaigns the platform did not set up, mirrored from connected seller accounts), "managed" (campaigns authored or adopted through the platform), or "all" (both — the default). The default status lens (non-terminal: ACTIVE, DRAFT, PAUSED) is what keeps mirrored history out of the ambient list; browse tracked scale through the connected-account relationship rollup.'
          example: managed
          default: all
          allOf:
          - $ref: '#/components/schemas/CampaignManagementFilter'
        description: 'Filter by management state: "tracked" (campaigns the platform did not set up, mirrored from connected seller accounts), "managed" (campaigns authored or adopted through the platform), or "all" (both — the default). The default status lens (non-terminal: ACTIVE, DRAFT, PAUSED) is what keeps mirrored history out of the ambient list; browse tracked scale through the connected-account relationship rollup.'
      - in: query
        name: mediaBuyStatus
        schema:
          description: Filter to only campaigns that have at least one media buy matching any of the given statuses
          anyOf:
          - type: array
            items:
              type: string
              enum:
              - DRAFT
              - PENDING_APPROVAL
              - INPUT_REQUIRED
              - ACTIVE
              - PAUSED
              - COMPLETED
              - CANCELED
              - FAILED
              - REJECTED
              - ARCHIVED
          - type: string
            enum:
            - DRAFT
            - PENDING_APPROVAL
            - INPUT_REQUIRED
            - ACTIVE
            - PAUSED
            - COMPLETED
            - CANCELED
            - FAILED
            - REJECTED
            - ARCHIVED
        description: Filter to only campaigns that have at least one media buy matching any of the given statuses
      - in: query
        name: includeArchived
        schema:
          description: 'Include archived (soft-deleted) campaigns in the response (default: false). Implicitly treated as true when the status filter contains ''ARCHIVED''.'
          type: string
          enum:
          - 'true'
          - 'false'
        description: 'Include archived (soft-deleted) campaigns in the response (default: false). Implicitly treated as true when the status filter contains ''ARCHIVED''.'
      - in: query
        name: fields
        schema:
          description: Optional response enrichment fields. Pass geo_metro_names to include display labels for geo_metros from the local label table.
          example: geo_metro_names
          anyOf:
          - type: string
          - type: array
            items:
              type: string
        description: Optional response enrichment fields. Pass geo_metro_names to include display labels for geo_metros from the local label table.
      - in: query
        name: cursor
        schema:
          description: Opaque pagination cursor returned by the previous response. When provided, skip is derived from the cursor and any explicit skip param is ignored.
          example: eyJza2lwIjoxMH0=
          type: string
        description: Opaque pagination cursor returned by the previous response. When provided, skip is derived from the cursor and any explicit skip param is ignored.
      - in: query
        name: take
        schema:
          description: Number of results to return (max 250)
          example: 50
          default: 50
          type: integer
          maximum: 250
          minimum: 1
        description: Number of results to return (max 250)
      - in: query
        name: skip
        schema:
          description: Number of results to skip for pagination
          example: 0
          default: 0
          type: integer
          minimum: 0
          maximum: 9007199254740991
        description: Number of results to skip for pagination
      responses:
        '200':
          description: List campaigns
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignListResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      operationId: createCampaign
      summary: Create campaign
      description: Create a new campaign. Optional fields like discoveryId and performanceConfig can be provided at creation or set later via update. The `brief` and `name` fields are screened by the content-moderation engine and rejected with 422 `CONTENT_MODERATION_BLOCKED` on policy violation.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCampaignOpenApiBody'
      responses:
        '201':
          description: Create campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Content rejected by moderation policy. The `brief` or `name` matched a prompt-injection / jailbreak / hate-speech / CSAM / violence / misinformation pattern, or failed the business-context gate.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}:
    put:
      operationId: updateCampaign
      summary: Update campaign
      description: Update an existing campaign. All fields are optional. The `brief` and `name` fields are screened by the content-moderation engine and rejected with 422 `CONTENT_MODERATION_BLOCKED` on policy violation.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCampaignOpenApiBody'
      responses:
        '200':
          description: Update campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Content rejected by moderation policy. See `POST /campaigns` for full criteria.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      operationId: getCampaign
      summary: Get campaign
      description: Get detailed information about a specific campaign.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: query
        name: fresh
        schema:
          description: When true, bypasses the server-side media-buy query cache for this request. Use after a successful lifecycle write when immediate authoritative local readback is required.
          example: 'true'
          anyOf:
          - type: boolean
          - type: string
            enum:
            - 'true'
            - 'false'
        description: When true, bypasses the server-side media-buy query cache for this request. Use after a successful lifecycle write when immediate authoritative local readback is required.
      - in: query
        name: mediaBuyId
        schema:
          description: Filter the embedded `mediaBuys[]` array to only the media buys with these IDs. Accepts a single ID (`?mediaBuyId=mb_X`) or repeated values (`?mediaBuyId=mb_X&mediaBuyId=mb_Y`). The campaign object itself is unchanged; only the nested media buys are narrowed. Use to drill into specific buys without loading every buy on the campaign (helps when the full tree exceeds an LLM context window). `mediaBuyRefs` still lists every buy on the campaign so callers can discover IDs.
          example: mb_ETBn4gJ9Wu
          anyOf:
          - type: array
            items:
              type: string
          - type: string
        description: Filter the embedded `mediaBuys[]` array to only the media buys with these IDs. Accepts a single ID (`?mediaBuyId=mb_X`) or repeated values (`?mediaBuyId=mb_X&mediaBuyId=mb_Y`). The campaign object itself is unchanged; only the nested media buys are narrowed. Use to drill into specific buys without loading every buy on the campaign (helps when the full tree exceeds an LLM context window). `mediaBuyRefs` still lists every buy on the campaign so callers can discover IDs.
      - in: query
        name: includePropertyLists
        schema:
          description: When true, embed a `propertyLists` aggregate showing the include/exclude lists actually applied to this campaign via its media-buy packages. Defaults to false to keep the response small. To fetch the aggregate without the rest of the campaign, use `GET /campaigns/:campaignId/property-lists`.
          example: 'true'
          anyOf:
          - type: boolean
          - type: string
            enum:
            - 'true'
            - 'false'
        description: When true, embed a `propertyLists` aggregate showing the include/exclude lists actually applied to this campaign via its media-buy packages. Defaults to false to keep the response small. To fetch the aggregate without the rest of the campaign, use `GET /campaigns/:campaignId/property-lists`.
      - in: query
        name: includeProductDetails
        schema:
          description: When false, strips `formatOptions` from each product in `mediaBuys[].products[]`. Defaults to true (full product details included). Set to false when the goal is to read `packageId` values or other non-product fields and the full product payload would cause response truncation.
          example: 'false'
          anyOf:
          - type: boolean
          - type: string
            enum:
            - 'true'
            - 'false'
        description: When false, strips `formatOptions` from each product in `mediaBuys[].products[]`. Defaults to true (full product details included). Set to false when the goal is to read `packageId` values or other non-product fields and the full product payload would cause response truncation.
      - in: query
        name: fields
        schema:
          description: Optional response enrichment fields. Pass geo_metro_names to include display labels for geo_metros from the local label table.
          example: geo_metro_names
          anyOf:
          - type: string
          - type: array
            items:
              type: string
        description: Optional response enrichment fields. Pass geo_metro_names to include display labels for geo_metros from the local label table.
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      responses:
        '200':
          description: Get campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      operationId: deleteCampaign
      summary: Delete campaign
      description: Delete a campaign.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      responses:
        '204':
          description: No content
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/delivery:
    get:
      operationId: getDirectedCampaignDelivery
      summary: Get campaign delivery (alpha)
      description: Read live delivery through a tracked campaign's connected seller account or an unambiguous single-storefront platform campaign's persisted provider media-buy identity. The latest totals are persisted; PostHog-gated alpha with no upstream write.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: query
        name: startDate
        schema:
          description: Inclusive reporting start. Defaults to the subscription backfill boundary, capped at one year ago.
          example: '2026-07-10'
          type: string
          format: date
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
        description: Inclusive reporting start. Defaults to the subscription backfill boundary, capped at one year ago.
      - in: query
        name: endDate
        schema:
          description: Inclusive reporting end. Defaults to yesterday because same-day platform data may be incomplete.
          example: '2026-07-10'
          type: string
          format: date
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$
        description: Inclusive reporting end. Defaults to yesterday because same-day platform data may be incomplete.
      - in: query
        name: placementBreakdown
        schema:
          description: Request canonical AdCP by_placement delivery rows when the product declares placement reporting support.
          type: string
          enum:
          - 'true'
          - 'false'
        description: Request canonical AdCP by_placement delivery rows when the product declares placement reporting support.
      - in: query
        name: placementLimit
        schema:
          description: Maximum placement rows per package. Requires placementBreakdown=true.
          type: integer
          maximum: 9007199254740991
          minimum: 1
        description: Maximum placement rows per package. Requires placementBreakdown=true.
      - in: query
        name: placementSortBy
        schema:
          description: Metric used to order placement rows before applying placementLimit.
          type: string
          enum:
          - impressions
          - spend
          - clicks
          - conversions
      - in: path
        name: campaignId
        schema:
          type: string
          minLength: 1
        required: true
      responses:
        '200':
          description: Get campaign delivery (alpha)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DirectedCampaignDeliveryResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: The directed-campaigns alpha is not enabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No directed campaign mirror with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/execute:
    post:
      operationId: executeCampaign
      summary: Execute campaign
      description: 'Launch/execute a campaign to start delivering ads. Pass `debug: true` in the request body to include detailed debug information in error responses.'
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecuteCampaignBody'
      responses:
        '200':
          description: Execute campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignStatusChangeResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/pause:
    post:
      operationId: pauseCampaign
      summary: Pause campaign
      description: Pause a running campaign. Cascades to all active media buys and reports the per-media-buy outcome.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      responses:
        '200':
          description: Pause campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignCascadeResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/reactivate:
    post:
      operationId: reactivateCampaign
      summary: Reactivate campaign
      description: Reactivate a paused campaign. The campaign must be PAUSED. Cascades to all paused media buys and reports the per-media-buy outcome.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      responses:
        '200':
          description: Reactivate campaign
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignCascadeResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/auto-select-products:
    post:
      operationId: autoSelectProducts
      summary: Auto-select products
      description: Automatically select products for a performance campaign using a 3-tier strategy (scoring → measurability → CPM heuristic). Campaign must be in DRAFT status. Supports iterative refinement via ADCP-style refine directives — review results then re-call with include/omit/more_like_this to adjust selections. Replaces all previously selected products.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AutoSelectProductsRequest'
      responses:
        '200':
          description: Auto-select products
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutoSelectProductsResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /campaigns/{campaignId}/media-buy-status:
    get:
      operationId: getMediaBuyAdcpStatus
      summary: Get media buy ADCP status
      description: Poll ADCP sales agents for the live status of all media buys in a campaign. Returns current ADCP status for each media buy and updates local status when changes are detected.
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: id
        schema:
          description: Unique identifier for the campaign
          example: cmp_987654321
          type: string
          minLength: 1
        required: true
        description: Unique identifier for the campaign
      responses:
        '200':
          description: Get media buy ADCP status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetAdcpStatusOutput'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /media-buys/{mediaBuyId}:
    get:
      operationId: getMediaBuy
      summary: Get media buy
      description: 'Get a single media buy with its why-visibility fields: pendingReason (why it is not delivering yet and since when), buyer-safe errorCode with ownership, the sanitized source message, forwardedAt, and the buyerReference support handle. One call answers "why is my buy stuck". Use GET /campaigns/{campaignId} for packages, products, and delivery.'
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: mediaBuyId
        schema:
          description: Buyer media buy ID
          example: mb_ETBn4gJ9Wu
          type: string
          minLength: 1
        required: true
        description: Buyer media buy ID
      responses:
        '200':
          description: Get media buy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuyerMediaBuyResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Caller is not a buyer — the route is mounted behind the buyer role guard.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No media buy with this id owned by the caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      operationId: updateMediaBuy
      summary: Update media buy
      description: 'Partially update a single media buy, resolved top-level by mediaBuyId — the owning campaign is resolved server-side, consistent with GET /media-buys/{mediaBuyId}. Rejected outright (not a warning) when it would violate a campaign invariant: currency (a product''s replacement pricing option settles in a different currency than this buy), budget headroom (the update would exceed the campaign''s remaining all-in budget), or mode compatibility (the campaign is directed and has no platform-managed update path). A flight date outside the campaign''s current window is not a failure — the campaign flight is widened to cover it and a warning is returned.'
      tags:
      - Campaigns
      security:
      - bearerAuth: []
      parameters:
      - in: path
        name: mediaBuyId
        schema:
          description: Buyer media buy ID
          example: mb_ETBn4gJ9Wu
          type: string
          minLength: 1
        required: true
        description: Buyer media buy ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMediaBuyRequest'
      responses:
        '200':
          description: Update media buy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateMediaBuyResponse'
      

# --- truncated at 32 KB (633 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/scope3/refs/heads/main/openapi/scope3-campaigns-api-openapi.yml