Skip to main content

Overview

v2 reporting metrics are delivery numbers (impressions, spend, clicks, conversions, derived rates) rolled up across a hierarchical tree: account → advertiser → campaign → media buy → package. Every request hits a single endpoint and resolves the same tree before joining BigQuery metrics on top.
GET /api/buyer/reporting/metrics
It returns either a hierarchical summary (advertiser → campaign → media buy → package) or a time-series (one row per leaf × day), and can also generate a CSV with a 7-day signed download URL. All metrics come from BigQuery; the hierarchy and access control come from Postgres.
Hourly delivery reporting is not supported on /api/buyer/reporting/metrics — this endpoint is day-grain only. There is no granularity parameter; passing one will be ignored. For hourly event counts (conversions, clicks, impressions tracked via your own pixel/event sources), use GET /api/buyer/advertisers/:id/events/summary instead.
v2 deliberately collapses what was a sprawl of v1 tools (get_campaign_summary, export_campaign_data, analyze_tactics, etc.) into one endpoint with a view switch. The same shape works for dashboards, agent prompts, and BI exports.

The hierarchy

Every metrics request resolves the same four-level tree before joining BigQuery metrics on top:
Account (customer)
└── Advertiser (seat)
    └── Campaign
        └── Media Buy
            └── Package (one per product within the buy)
Filters cascade: advertiserId narrows to that seat, campaignId narrows to one campaign within an advertiser, and the system enforces that the campaign belongs to the advertiser when both are passed.

Available metrics

Every level reports the same metric block:
MetricTypeNotes
impressionsintSum across deduped daily reports
spendnumberSum in USD
clicksint
viewsintViewable impressions (MRC-viewable subset of impressions, per AdCP). Divide by impressions for viewability rate.
completedViewsintVideo/audio completions (qualified by view_duration_seconds when set on the goal)
conversionsint
leadsint
videoCompletionsint
ecpmnumber | null(spend × 1000) / impressions
cpcnumber | nullspend / clicks
ctrnumber | nullclicks / impressions
completionRatenumber | nullcompletedViews / views
Derived metrics are null when their denominator is zero.

Query parameters

ParamTypeDefaultNotes
advertiserIdstringSeat ID
campaignIdstringMust belong to advertiserId if both passed
startDateYYYY-MM-DDderived from days
endDateYYYY-MM-DDtoday
daysint 0..9070 = full campaign timeframe (summary only)
viewsummary | timeseriessummary
breakdowncampaign | mediaBuy | packageUI hint only — does not affect data. Required for MCP / agent callers.
downloadboolfalseWhen true, returns a signed CSV URL
demoboolfalseReturns auto-generated demo data — useful for sandbox UIs
JSON time-series responses are capped at 7 days. If you need a wider window, use ?download=true&view=timeseries to get a CSV. The CSV path supports up to 90 days.

Hierarchical summary (view=summary)

curl "https://api.agentic.scope3.com/api/buyer/reporting/metrics?days=14&advertiserId=42" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
{
  "advertisers": [
    {
      "advertiserId": "42",
      "advertiserName": "Acme Corp",
      "metrics": { "impressions": 12345, "spend": 678.9, "...": "..." },
      "campaigns": [
        {
          "campaignId": "camp_001",
          "campaignName": "Spring Awareness",
          "metrics": { "...": "..." },
          "mediaBuys": [
            {
              "mediaBuyId": "mb_abc",
              "name": "CTV Always-On",
              "status": "ACTIVE",
              "budget": 50000,
              "metrics": { "...": "..." },
              "packages": [
                {
                  "packageId": "pkg_xyz",
                  "productId": "prod_xyz",
                  "productName": "Premium CTV — Network Supply",
                  "metrics": { "...": "..." }
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "totals": { "...": "..." },
  "periodStart": "2026-04-12",
  "periodEnd": "2026-04-25"
}

Time-series (view=timeseries)

curl "https://api.agentic.scope3.com/api/buyer/reporting/metrics?view=timeseries&days=7" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
Returns flat rows — one per leaf per day — sorted by date ascending. Every row carries the full ancestry so a chart can group by any level without an extra join:
{
  "timeseries": [
    {
      "date": "2026-04-19",
      "advertiserId": "42",
      "advertiserName": "Acme Corp",
      "campaignId": "camp_001",
      "campaignName": "Spring Awareness",
      "mediaBuyId": "mb_abc",
      "mediaBuyName": "CTV Always-On",
      "mediaBuyStatus": "ACTIVE",
      "packageId": "pkg_xyz",
      "productId": "prod_xyz",
      "productName": "Premium CTV — Network Supply",
      "metrics": { "...": "..." }
    }
  ],
  "totals": { "...": "..." },
  "periodStart": "2026-04-19",
  "periodEnd": "2026-04-25"
}

CSV export (download=true)

Append download=true to either view to generate a CSV in GCS and return a signed URL:
curl "https://api.agentic.scope3.com/api/buyer/reporting/metrics?view=timeseries&days=90&download=true" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
{
  "downloadUrl": "https://storage.googleapis.com/scope3-reporting-exports/...",
  "expiresAt": "2026-05-02T00:00:00Z",
  "fileName": "reporting-2026-01-25-2026-04-25.csv",
  "rowCount": 1842
}
  • The signed URL expires in 7 days.
  • CSV columns mirror the JSON shape (one row per leaf for summary, one row per leaf × day for time-series).
  • Download is the recommended path for ranges longer than 7 days when view=timeseries.
The signed download URL is itself a bearer credential. Anyone who has the URL can download the export — there is no additional auth check beyond URL knowledge until expiry.
  • Treat downloadUrl like a password or API key: never paste it into chat, tickets, screenshots, public dashboards, or unencrypted email.
  • Don’t log the URL in long-lived application logs.
  • Hand the URL to the consuming system over a secure channel and have that system fetch immediately rather than queuing it for later.
  • The export contains advertiser, campaign, and spend data — treat the downloaded CSV itself as sensitive once retrieved.

Group-by guidance

The endpoint always returns the full hierarchy. The optional breakdown query param is a UI rendering hint consumed by the MCP App reporting view — it sets the default group-by in the chart and table. The data is identical regardless. MCP / agent callers (api_call, get_reporting_metrics) are required to provide it so the rendered output isn’t ambiguous; direct REST callers may omit it. Audit logs are a sibling read endpoint for the buyer activity feed:
GET /api/buyer/audit-logs
The buyer feed surfaces meaningful actions on CAMPAIGN, CREATIVE, MEDIA_BUY, and other resource types. Filterable by startDate / endDate, advertiserId, campaignId, and resourceTypes. Returns up to 500 logs per page (default 50) plus a total for pagination.
curl "https://api.agentic.scope3.com/api/buyer/audit-logs?campaignId=camp_001&take=100" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
Use audit logs for “who did what, when” questions — that data lives in Postgres and is recorded in real time. Use /reporting/metrics for performance numbers — that data lives in BigQuery and is aggregated nightly from agent-reported delivery.

How delivery data flows

Reporting metrics depend on the sales agent reporting actual delivery back to Scope3. Three transports are supported and chosen at agent registration time via the SALES agent’s reportingType field. See the Storefront onboarding guide for the registration syntax — the reporting transports themselves follow the ADCP optimization & reporting spec.

WEBHOOK (default)

Agent posts ADCP get_media_buy_delivery-shaped payloads to a Scope3 webhook URL embedded in the original create_media_buy call. Best for most integrations.

BUCKET

Agent writes JSON / JSONL / CSV / Parquet files to S3, GCS, or Azure Blob. Scope3 watches the path and ingests on landing. Best for high-volume batch reporting.

POLLING

Scope3 calls the agent’s get_media_buy_delivery on a DAILY or MONTHLY schedule. Best for legacy systems without outbound webhooks.
All three transports land in the same BigQuery table (media_buy_reporting_data) and are deduped per (reporting_date, media_buy_id) keeping the most recent received_at. From there, /reporting/metrics does the math.

Webhook delivery

When a SALES agent is registered with reportingType: WEBHOOK, Scope3 issues a per-media-buy webhook URL inside the original create_media_buy call. The agent posts ADCP get_media_buy_delivery-shaped payloads back to that URL on its own cadence. Every request is signed and timestamped:
HeaderPurpose
X-Scope3-SignatureHMAC-SHA256 of timestamp + "." + raw-body, hex-encoded, computed with the shared webhook secret.
X-Scope3-TimestampRFC3339 timestamp of the request. Receivers MUST reject requests where `now − timestamp` exceeds 5 minutes to defeat replay.
X-Scope3-Webhook-IdIdempotency key. Scope3 deduplicates by this value across retries.
Operational rules:
  • The shared secret is provisioned at agent registration and stored in Google Secret Manager alongside the agent’s auth credentials. Treat it as production-grade secret material — never log, commit, or paste it.
  • Verify the signature in constant time before parsing the body. Reject unsigned, mistimed, or duplicate requests.
  • Rotate the secret on a documented schedule and on any suspected exposure. Scope3 supports a brief overlap window where both old and new secrets validate so in-flight deliveries are not lost.
See the Storefront onboarding guide for the registration syntax and matching rules on the agent side.

Access control

  • The customer ID is taken from the auth context — buyers can only read metrics for media buys their customer owns.
  • When advertiserId is provided, the platform double-checks seat access before issuing any BigQuery query.
  • BigQuery itself filters every query on customer_id as well, so a missing Postgres ACL cannot leak data.

Demo mode

Pass ?demo=true to get a deterministic, multi-advertiser fixture with realistic campaign and product names. Useful for storybook / sandbox screens without any seeded data, and for teaching agents how the schema looks before running a real query. Demo data is capped to a 90-day window.