> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get reporting

> Get reporting metrics across all advertisers and campaigns. The response is hierarchical for view=summary, flat leaf-by-day rows for view=timeseries, or a signed CSV download descriptor when download=true.



## OpenAPI

````yaml /v2/buyer-api-v2.yaml get /reporting/metrics
openapi: 3.0.0
info:
  title: Scope3 Buyer 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
security: []
tags:
  - name: Signup
    description: Request reviewed access to Interchange
  - name: Account
    description: Account management, service tokens, and preferences
  - name: Asks
    description: >-
      What you are waiting on Scope3 for — support, product, and supply asks in
      one list
  - name: Advertisers
    description: Manage advertisers
  - name: Product Discovery
    description: Discover and select products
  - name: Campaigns
    description: Manage advertising campaigns
  - name: Creatives
    description: Build, manage, and sync campaign creatives via AdCP Creative Protocol
  - name: Reporting
    description: Access performance metrics
  - name: Event Sources
    description: >-
      Manage event source configurations and log conversion/marketing events for
      attribution
  - name: Property Lists
    description: Validate property lists against AAO registry
  - name: Sales Agents
    description: View and connect sales agents
  - name: Measurement
    description: Measurement sources, records, context, and freshness
  - name: Syndication
    description: Syndicate resources to ADCP agents
  - name: Tasks
    description: Track async operation status
  - name: Buyer Billing
    description: >-
      Consolidated invoicing for buyers — invoices and pending invoice items
      issued by Scope3 across the buyer customer.
  - name: MCP
    description: Model Context Protocol endpoints for AI agents
paths:
  /reporting/metrics:
    get:
      tags:
        - Reporting
      summary: Get reporting
      description: >-
        Get reporting metrics across all advertisers and campaigns. The response
        is hierarchical for view=summary, flat leaf-by-day rows for
        view=timeseries, or a signed CSV download descriptor when download=true.
      operationId: getReportingMetrics
      parameters:
        - in: query
          name: advertiserId
          schema:
            description: Filter to campaigns on this advertiser
            type: string
          description: Filter to campaigns on this advertiser
        - in: query
          name: campaignId
          schema:
            description: >-
              Filter to a specific campaign. If advertiserId is also provided,
              the campaign must belong to that advertiser
            type: string
          description: >-
            Filter to a specific campaign. If advertiserId is also provided, the
            campaign must belong to that advertiser
        - in: query
          name: startDate
          schema:
            description: Start date in ISO format (YYYY-MM-DD)
            example: '2025-01-01'
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Start date in ISO format (YYYY-MM-DD)
        - in: query
          name: endDate
          schema:
            description: End date in ISO format (YYYY-MM-DD)
            example: '2025-01-31'
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: End date in ISO format (YYYY-MM-DD)
        - in: query
          name: days
          schema:
            description: >-
              Number of days to include (default: 7, max: 90). Use 0 for the
              full campaign timeframe.
            example: 7
            default: 7
            type: integer
            minimum: 0
            maximum: 90
          description: >-
            Number of days to include (default: 7, max: 90). Use 0 for the full
            campaign timeframe.
        - in: query
          name: view
          schema:
            description: >-
              Response format: "summary" for hierarchical
              advertiser/campaign/media-buy/package breakdown, "timeseries" for
              flat rows with one entry per (advertiser → campaign → media buy →
              package) × day
            default: summary
            type: string
            enum:
              - summary
              - timeseries
          description: >-
            Response format: "summary" for hierarchical
            advertiser/campaign/media-buy/package breakdown, "timeseries" for
            flat rows with one entry per (advertiser → campaign → media buy →
            package) × day
        - in: query
          name: displayCurrency
          schema:
            description: >-
              ISO-4217 currency for the consolidated (FX-converted)
              cross-advertiser rollup. Defaults to the buyer home currency when
              the advertisers share one; must be a platform-supported currency.
              Summary view only. The converted figures are display estimates,
              never billing inputs
            example: EUR
            type: string
            pattern: ^[A-Za-z]{3}$
          description: >-
            ISO-4217 currency for the consolidated (FX-converted)
            cross-advertiser rollup. Defaults to the buyer home currency when
            the advertisers share one; must be a platform-supported currency.
            Summary view only. The converted figures are display estimates,
            never billing inputs
        - in: query
          name: download
          schema:
            description: >-
              When true, generates a CSV file and returns a signed download URL
              instead of JSON data
            default: false
            type: boolean
          description: >-
            When true, generates a CSV file and returns a signed download URL
            instead of JSON data
        - in: query
          name: demo
          schema:
            description: >-
              When true, returns auto-generated demo data instead of querying
              real data sources
            default: false
            type: boolean
          required: true
          description: >-
            When true, returns auto-generated demo data instead of querying real
            data sources
      responses:
        '200':
          description: Get reporting
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/ReportingMetricsResponse'
                  - $ref: '#/components/schemas/ReportingMetricsTimeseriesResponse'
                  - $ref: '#/components/schemas/ReportingMetricsExportResponse'
        '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'
      security:
        - bearerAuth: []
components:
  schemas:
    ReportingMetricsResponse:
      description: >-
        Hierarchical reporting response: advertiser → campaign → media buy →
        package
      type: object
      properties:
        advertisers:
          description: Advertiser-level reporting data
          type: array
          items:
            $ref: '#/components/schemas/AdvertiserReporting'
        totals:
          description: >-
            Aggregated totals across all advertisers, exact and native. Money
            metrics are present only when every advertiser shares one currency
            (totalsCurrency); when currencies mix they are null — see the
            consolidated block for the converted rollup
          allOf:
            - $ref: '#/components/schemas/ReportingTotalsMetrics'
        totalsCurrency:
          description: >-
            ISO-4217 currency the native totals are denominated in; null when
            the advertisers span multiple currencies (money metrics in totals
            are then null)
          nullable: true
          type: string
        consolidated:
          description: >-
            FX-converted cross-advertiser rollup in the display currency
            (explicit displayCurrency, or the buyer home currency when
            resolvable). Omitted when no conversion applies (single-currency
            buyer viewing natively). Display estimate only — never a billing
            input
          nullable: true
          allOf:
            - $ref: '#/components/schemas/ConsolidatedReporting'
        periodStart:
          description: Start of the reporting period (YYYY-MM-DD)
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
        periodEnd:
          description: End of the reporting period (YYYY-MM-DD)
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
      required:
        - advertisers
        - totals
        - totalsCurrency
        - periodStart
        - periodEnd
      additionalProperties: false
    ReportingMetricsTimeseriesResponse:
      description: >-
        Time-series reporting response: flat leaf × day rows (same hierarchy
        fields as the summary view, plus date)
      type: object
      properties:
        timeseries:
          description: >-
            Flat rows: one entry per (advertiser → campaign → media buy →
            package) × day, sorted by date ascending
          type: array
          items:
            $ref: '#/components/schemas/TimeseriesEntry'
        totals:
          description: >-
            Aggregated totals across the entire period. Money metrics are null
            when the emitted rows span currencies
          allOf:
            - $ref: '#/components/schemas/ReportingTotalsMetrics'
        totalsCurrency:
          description: >-
            ISO-4217 currency the returned-row totals are denominated in; null
            when there are no rows or emitted rows span multiple currencies
          nullable: true
          type: string
        periodStart:
          description: Start of the reporting period (YYYY-MM-DD)
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
        periodEnd:
          description: End of the reporting period (YYYY-MM-DD)
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
      required:
        - timeseries
        - totals
        - totalsCurrency
        - periodStart
        - periodEnd
      additionalProperties: false
    ReportingMetricsExportResponse:
      description: Response containing a signed URL to download the exported CSV
      type: object
      properties:
        downloadUrl:
          description: Signed URL to download the CSV file (expires in 7 days)
          type: string
          format: uri
        expiresAt:
          description: ISO 8601 timestamp when the download URL expires
          type: string
          format: date-time
          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])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
        fileName:
          description: Name of the generated CSV file
          type: string
        rowCount:
          description: Number of data rows in the CSV (excluding header)
          type: integer
          minimum: 0
          maximum: 9007199254740991
      required:
        - downloadUrl
        - expiresAt
        - fileName
        - rowCount
      additionalProperties: false
    ErrorResponse:
      description: Standard error response
      type: object
      properties:
        data:
          type: string
          nullable: true
          enum:
            - null
        error:
          $ref: '#/components/schemas/ApiError'
      required:
        - data
        - error
      additionalProperties: false
    AdvertiserReporting:
      description: Advertiser-level reporting metrics with campaign breakdown
      type: object
      properties:
        advertiserId:
          description: Advertiser identifier
          type: string
        advertiserName:
          description: Advertiser name
          type: string
        currency:
          description: >-
            ISO-4217 currency used for money under this advertiser (the
            advertiser's locked primary currency); source delivery spend is
            normalized to it when an applicable FX rate is available
          type: string
        metrics:
          $ref: '#/components/schemas/ReportingMetrics'
        campaigns:
          description: Campaign-level breakdown
          type: array
          items:
            $ref: '#/components/schemas/CampaignReporting'
      required:
        - advertiserId
        - advertiserName
        - currency
        - metrics
        - campaigns
      additionalProperties: false
    ReportingTotalsMetrics:
      description: >-
        Reporting totals; money metrics are null when returned denominations mix
        or no denomination is available
      type: object
      properties:
        impressions:
          description: Total impressions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        spend:
          description: >-
            Total spend (gross, fee-inclusive — the buyer denomination) in the
            currency named by totalsCurrency; null when the returned data spans
            currencies or has no denomination
          nullable: true
          type: number
          minimum: 0
        clicks:
          description: Total clicks
          type: integer
          minimum: 0
          maximum: 9007199254740991
        views:
          description: >-
            Viewable impressions (the MRC-viewable subset of impressions, per
            AdCP). For viewability rate, divide by impressions.
          type: integer
          minimum: 0
          maximum: 9007199254740991
        completedViews:
          description: >-
            Video/audio completions (qualified by view_duration_seconds when set
            on the goal)
          type: integer
          minimum: 0
          maximum: 9007199254740991
        conversions:
          description: Total conversions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        leads:
          description: Total leads
          type: integer
          minimum: 0
          maximum: 9007199254740991
        videoCompletions:
          description: Total video completions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        conversionValue:
          description: >-
            Total conversion value in the currency named by totalsCurrency; null
            when the returned data spans currencies or has no denomination
          nullable: true
          type: number
          minimum: 0
        ecpm:
          description: >-
            Effective CPM: (spend * 1000) / impressions, derived from spend in
            this surface's denomination (gross fee-inclusive on buyer surfaces);
            null when impressions is 0
          nullable: true
          type: number
        cpc:
          description: >-
            Cost per click: spend / clicks, derived from spend in this surface's
            denomination (gross fee-inclusive on buyer surfaces); null when
            clicks is 0
          nullable: true
          type: number
        ctr:
          description: 'Click-through rate: clicks / impressions'
          nullable: true
          type: number
        completionRate:
          description: >-
            Completion rate: completedViews / impressions (completions per paid
            impression; impressions is the paid unit for CPM video)
          nullable: true
          type: number
        cpa:
          description: >-
            Cost per acquisition: spend / conversions, derived from spend in
            this surface's denomination (gross fee-inclusive on buyer surfaces);
            null when conversions is 0
          nullable: true
          type: number
        roas:
          description: >-
            Return on ad spend: conversionValue / spend; null when spend is 0,
            or when there is no conversion signal at all (conversionValue and
            conversions are both 0). Reports a true 0 when conversions are real
            but attributed value is zero. conversionValue carries no platform
            fee while spend is gross fee-inclusive on buyer surfaces, so buyer
            ROAS reads lower than storefront ROAS for identical delivery — the
            same denomination difference that already separates buyer and
            storefront cpc/ecpm.
          nullable: true
          type: number
      required:
        - impressions
        - spend
        - clicks
        - views
        - completedViews
        - conversions
        - leads
        - videoCompletions
        - conversionValue
        - ecpm
        - cpc
        - ctr
        - completionRate
        - cpa
        - roas
      additionalProperties: false
    ConsolidatedReporting:
      description: >-
        FX-converted cross-advertiser rollup (display estimate only — never
        billing)
      type: object
      properties:
        totals:
          description: >-
            Cross-advertiser totals in the display currency: counts summed
            natively, money converted per advertiser then summed; money-derived
            metrics are null when any advertiser could not be converted
          allOf:
            - $ref: '#/components/schemas/ReportingTotalsMetrics'
        byAdvertiser:
          description: >-
            Per-advertiser converted spend so the consolidated total is
            auditable row by row
          type: array
          items:
            type: object
            properties:
              advertiserId:
                type: string
              currency:
                description: The advertiser native currency this row converted from
                type: string
              spend:
                description: >-
                  This advertiser spend converted to the display currency; null
                  when its pair had no rate
                nullable: true
                type: number
            required:
              - advertiserId
              - currency
              - spend
            additionalProperties: false
        conversionInfo:
          $ref: '#/components/schemas/ReportingConversionInfo'
      required:
        - totals
        - byAdvertiser
        - conversionInfo
      additionalProperties: false
    TimeseriesEntry:
      description: >-
        One row of metrics for a single (advertiser → campaign → media buy →
        package) leaf on a single day
      type: object
      properties:
        date:
          description: Reporting date (YYYY-MM-DD)
          example: '2025-01-15'
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
        advertiserId:
          description: Advertiser identifier
          type: string
        advertiserName:
          description: Advertiser name
          type: string
        currency:
          description: >-
            ISO-4217 currency that actually denominates the row's spend, eCPM,
            and CPC; normally the advertiser's locked primary currency, or the
            reported source currency when FX conversion is unavailable
          type: string
        campaignId:
          description: Campaign identifier
          type: string
        campaignName:
          description: Campaign name
          type: string
        management:
          description: >-
            Management state of the row's campaign: "tracked" (mirrored
            read-only from a connected seller account) or "managed" (authored or
            adopted through the platform). Rows without a campaign are managed.
          allOf:
            - $ref: '#/components/schemas/CampaignManagement'
        mediaBuyId:
          description: Media buy identifier
          type: string
        mediaBuyName:
          description: Media buy name
          type: string
        mediaBuyStatus:
          description: Media buy status
          type: string
        packageId:
          description: Package identifier. Empty string when the media buy has no packages.
          type: string
        productId:
          description: Product identifier for this package. Empty string when no package.
          type: string
        productName:
          description: >-
            Human-readable product name for this package. Empty string when no
            package.
          type: string
        metrics:
          $ref: '#/components/schemas/ReportingMetrics'
      required:
        - date
        - advertiserId
        - advertiserName
        - currency
        - campaignId
        - campaignName
        - management
        - mediaBuyId
        - mediaBuyName
        - mediaBuyStatus
        - packageId
        - productId
        - productName
        - metrics
      additionalProperties: false
    ApiError:
      description: Structured error object
      type: object
      properties:
        code:
          description: Machine-readable error code
          type: string
        message:
          description: Human-readable error message
          type: string
        field:
          description: Field path associated with the error
          type: string
        details:
          description: Additional error context
          type: object
          additionalProperties: {}
      required:
        - code
        - message
      additionalProperties: false
    ReportingMetrics:
      description: Raw and calculated reporting metrics
      type: object
      properties:
        impressions:
          description: Total impressions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        spend:
          description: >-
            Total spend, denominated in the advertiser's primary currency (every
            campaign and buy is stamped in that currency; USD only for USD
            advertisers). On BUYER surfaces spend is GROSS (fee-inclusive):
            seller-reported net delivered spend is grossed up at read time at
            each buy's own pinned terms (a buy without pinned terms reports net
            as stored). On STOREFRONT surfaces spend stays NET as the seller
            reported it.
          type: number
          minimum: 0
        clicks:
          description: Total clicks
          type: integer
          minimum: 0
          maximum: 9007199254740991
        views:
          description: >-
            Viewable impressions (the MRC-viewable subset of impressions, per
            AdCP). For viewability rate, divide by impressions.
          type: integer
          minimum: 0
          maximum: 9007199254740991
        completedViews:
          description: >-
            Video/audio completions (qualified by view_duration_seconds when set
            on the goal)
          type: integer
          minimum: 0
          maximum: 9007199254740991
        conversions:
          description: Total conversions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        leads:
          description: Total leads
          type: integer
          minimum: 0
          maximum: 9007199254740991
        videoCompletions:
          description: Total video completions
          type: integer
          minimum: 0
          maximum: 9007199254740991
        conversionValue:
          description: >-
            Total conversion value (advertiser-attributed revenue from
            conversions), denominated the same as spend. Unlike spend,
            conversion value is never grossed up on buyer surfaces — it is
            advertiser revenue, not marketplace spend, so it carries no platform
            fee.
          type: number
          minimum: 0
        ecpm:
          description: >-
            Effective CPM: (spend * 1000) / impressions, derived from spend in
            this surface's denomination (gross fee-inclusive on buyer surfaces);
            null when impressions is 0
          nullable: true
          type: number
        cpc:
          description: >-
            Cost per click: spend / clicks, derived from spend in this surface's
            denomination (gross fee-inclusive on buyer surfaces); null when
            clicks is 0
          nullable: true
          type: number
        ctr:
          description: 'Click-through rate: clicks / impressions'
          nullable: true
          type: number
        completionRate:
          description: >-
            Completion rate: completedViews / impressions (completions per paid
            impression; impressions is the paid unit for CPM video)
          nullable: true
          type: number
        cpa:
          description: >-
            Cost per acquisition: spend / conversions, derived from spend in
            this surface's denomination (gross fee-inclusive on buyer surfaces);
            null when conversions is 0
          nullable: true
          type: number
        roas:
          description: >-
            Return on ad spend: conversionValue / spend; null when spend is 0,
            or when there is no conversion signal at all (conversionValue and
            conversions are both 0). Reports a true 0 when conversions are real
            but attributed value is zero. conversionValue carries no platform
            fee while spend is gross fee-inclusive on buyer surfaces, so buyer
            ROAS reads lower than storefront ROAS for identical delivery — the
            same denomination difference that already separates buyer and
            storefront cpc/ecpm.
          nullable: true
          type: number
      required:
        - impressions
        - spend
        - clicks
        - views
        - completedViews
        - conversions
        - leads
        - videoCompletions
        - conversionValue
        - ecpm
        - cpc
        - ctr
        - completionRate
        - cpa
        - roas
      additionalProperties: false
    CampaignReporting:
      description: Campaign-level reporting metrics with media buy breakdown
      type: object
      properties:
        campaignId:
          description: Campaign identifier
          type: string
        campaignName:
          description: Campaign name
          type: string
        management:
          description: >-
            Management state of this campaign: "tracked" (a campaign the
            platform did not set up, mirrored read-only from a connected seller
            account) or "managed" (authored or adopted through the platform).
            Buyer reporting currently includes managed campaigns only — tracked
            mirror delivery is excluded from this surface — so aggregates never
            silently mix the two states.
          allOf:
            - $ref: '#/components/schemas/CampaignManagement'
        metrics:
          $ref: '#/components/schemas/ReportingMetrics'
        mediaBuys:
          description: Media buy-level breakdown
          type: array
          items:
            $ref: '#/components/schemas/MediaBuyReporting'
      required:
        - campaignId
        - campaignName
        - management
        - metrics
        - mediaBuys
      additionalProperties: false
    ReportingConversionInfo:
      description: How the consolidated (FX-converted) figures were produced
      type: object
      properties:
        displayCurrency:
          description: ISO-4217 currency the consolidated block is expressed in
          type: string
        asOf:
          description: >-
            UTC calendar day whose locked rate-of-the-day was applied (one rate
            across the whole window)
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
        rateSource:
          description: Where the rates came from (the platform rate-of-day table)
          type: string
          enum:
            - fx_rate_snapshots
        rateBasis:
          description: >-
            Conversion basis: 'as-of' applies one current rate-of-the-day to the
            whole window (an estimate; a booked-rate basis is a later phase)
          type: string
          enum:
            - as-of
        rates:
          description: Every rate applied in this conversion
          type: array
          items:
            type: object
            properties:
              pair:
                description: BASEQUOTE pair, e.g. USDZAR = ZAR per USD
                type: string
              rate:
                description: QUOTE units per 1 BASE
                type: number
            required:
              - pair
              - rate
            additionalProperties: false
        unconverted:
          description: >-
            Native currencies that could NOT be converted (no rate available);
            their spend is excluded from the consolidated money totals rather
            than guessed
          type: array
          items:
            type: string
        estimate:
          description: >-
            Always true: the consolidated figures are display estimates, never
            billing or settlement inputs
          type: boolean
          enum:
            - true
      required:
        - displayCurrency
        - asOf
        - rateSource
        - rateBasis
        - rates
        - unconverted
        - estimate
      additionalProperties: false
    CampaignManagement:
      description: >-
        Whether the platform acts on this campaign. "tracked" = a campaign the
        platform did not set up, mirrored from a connected seller account: the
        shell is derived from the buys underneath, read-only, and updates
        automatically as the seller changes things. "managed" = a campaign
        authored or adopted through the platform (get_products →
        create_media_buy → update_media_buy) — the platform sends instructions
        down. In both states the execution system remains the source of truth.
      type: string
      enum:
        - tracked
        - managed
    MediaBuyReporting:
      description: Media buy-level reporting metrics with package breakdown
      type: object
      properties:
        mediaBuyId:
          description: Media buy identifier
          type: string
        name:
          description: Media buy name
          type: string
        status:
          description: Media buy status
          type: string
        budget:
          description: >-
            Total allocated budget for this media buy (sum of product budgets),
            GROSS (fee-inclusive — stored budgets are the buyer denomination
            since the gross-budgets flip). Legacy media buys created before fee
            terms were locked report their stored legacy (net) amount instead.
            Null if no budget set
          nullable: true
          type: number
        metrics:
          $ref: '#/components/schemas/ReportingMetrics'
        deliveryFxConversion:
          description: >-
            FX conversion applied to delivery spend when the source reported in
            a different currency from the advertiser primary currency. Null when
            no conversion was needed.
          nullable: true
          type: object
          properties:
            fromCurrency:
              description: ISO-4217 currency the source reported delivery spend in
              type: string
            rate:
              description: >-
                Multiplier applied to source spend to produce
                advertiser-currency spend (QUOTE per BASE, e.g. ZAR per USD)
              type: number
            asOfDate:
              description: UTC calendar date of the FX rate used
              type: string
              pattern: ^\d{4}-\d{2}-\d{2}$
            source:
              description: >-
                booked: rate locked in the spread ledger at buy time; snapshot:
                fx_rate_snapshots fallback on buy creation date
              type: string
              enum:
                - booked
                - snapshot
          required:
            - fromCurrency
            - rate
            - asOfDate
            - source
          additionalProperties: false
        packages:
          description: Package-level breakdown
          type: array
          items:
            $ref: '#/components/schemas/PackageReporting'
      required:
        - mediaBuyId
        - name
        - status
        - budget
        - metrics
        - packages
      additionalProperties: false
    PackageReporting:
      description: Package-level reporting metrics
      type: object
      properties:
        packageId:
          description: Package identifier
          type: string
        productId:
          description: Product identifier for this package
          nullable: true
          type: string
        productName:
          description: Human-readable product name for this package
          nullable: true
          type: string
        metrics:
          $ref: '#/components/schemas/ReportingMetrics'
      required:
        - packageId
        - productId
        - productName
        - metrics
      additionalProperties: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key or access token

````