Signals Coverage Proof

Signals coverage proof answers one practical question before you use a visitor lane or saved filter:

Do we have enough real people in this result, and did we keep non-human traffic measured separately?

Use it before making site changes, reporting coverage, planning content, or deciding whether a result is large enough to act on. It keeps three views side by side:

ViewWhat it provesWhy it matters
CoverageHow many first-party visitors match the saved filter.You know whether the result is large and fresh enough to use.
Lane separationHow much bot-classed traffic was measured separately from humans.Crawlers, monitors, previews, and automation do not become "people" by accident.
Match readinessHow many matched visitors have first-party identity signals.You can see whether the result has enough first-party signal without exposing raw email or phone values.

Coverage proof is an API surface: POST /api/signals/proof on the dashboard, authenticated with a dashboard session. There is no dashboard screen that renders it today — call it from a controlled workflow that holds a dashboard session, as below. (The csst_ token for the live Signals Feed is a separate surface with its own UI: Sites → select the property → Install tab → Stream token.)

Beginner Workflow

  1. Pick the site and date range: 24h, 7d, 30d, or 90d.
  2. Leave filters empty for site-wide proof, or add approved saved filters for a narrower result.
  3. Check coverage.segmentVisitors for the matched human-visitor count.
  4. Check coverage.botSuppressedVisitors and coverage.botRate to make sure non-human traffic was measured separately.
  5. Check answerEngine.topCrawledPages against answerEngine.topHumanPages to find coverage gaps.
  6. Turn the findings into a site, content, or instrumentation task. Do not expose the internal filter recipe to end users.

Copy-Paste: Request Proof

Run this from a dashboard-authenticated admin page or server route. The browser must already have a valid dashboard session cookie.

async function requestCoverageProof() {
  const response = await fetch('/api/signals/proof', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify({
      siteId: 'site_acd434d3',
      name: 'Docs coverage check',
      filters: [],
      topicIds: [],
      range: '30d',
    }),
  });

  if (!response.ok) {
    throw new Error(`Proof failed: ${response.status}`);
  }

  return response.json();
}

Field rules: siteId is optional — omit it (or send null) to prove across every site you can reach. name is trimmed and capped at 160 characters. filters and topicIds default to empty arrays. range is one of 24h, 7d, 30d, 90d and defaults to 30d.

Errors

StatusBodyMeaning
401UnauthorizedNo valid dashboard session.
404No sites available for coverage analysisThe account has no reachable sites.
404Site not foundThe siteId is not one of your reachable sites.
400Validation error + detailsBody failed schema validation.
409This segment is too large for a synchronous exact proof... + codeNarrow the property, range, or filters and retry. Proof is exact or it fails — it never returns an estimate.
500Internal server errorRetry with backoff.

Reading The Response

{
  "proof": {
    "generatedAt": 1778880000000,
    "name": "Docs coverage check",
    "siteId": "site_acd434d3",
    "siteNames": ["Example Site"],
    "filtersEvaluated": 0,
    "topicIdsEvaluated": 0,
    "coverage": {
      "range": "30d",
      "rangeStart": 1776288000000,
      "rangeEnd": 1778880000000,
      "specificity": "site_scope",
      "measuredAgainst": "first_party_visitors",
      "totalVisitors": 1420,
      "humanVisitors": 1310,
      "segmentVisitors": 1310,
      "botSuppressedVisitors": 110,
      "identifiedPeople": 410,
      "humanEvidencePeople": 1310,
      "matchablePeople": 390,
      "hashedIdentifierPeople": 210,
      "consentReadyIdentityPeople": 180,
      "totalEvents": 6200,
      "humanEvents": 5710,
      "botEvents": 490,
      "botRate": 7.9,
      "coverageScore": 100,
      "confidenceScore": 90,
      "matchabilityScore": 30,
      "identityReadinessScore": 14,
      "freshnessScore": 100
    },
    "answerEngine": {
      "aiAgentEvents": 16,
      "aiAgentVisitors": 9,
      "searchCrawlerEvents": 224,
      "searchCrawlerVisitors": 41,
      "topCrawledPages": [
        { "path": "/docs/getting-started", "events": 48, "visitors": 18, "avgBotScore": 84 }
      ],
      "topHumanPages": [
        { "path": "/account/settings", "events": 220, "visitors": 96 }
      ],
      "botRegistry": [
        { "name": "Known crawler", "category": "search_crawler", "events": 80, "visitors": 15, "pagesVisited": 9 }
      ],
      "recommendations": [
        {
          "type": "metadata",
          "priority": "high",
          "message": "High-human pages are not showing bot coverage yet: /account/settings."
        }
      ]
    },
    "readiness": {
      "coverageReady": true,
      "identityCoverageReady": true,
      "consentBound": true,
      "recommendedUse": "hashed_identifiers",
      "blockedReasons": [],
      "readyPeople": 180
    }
  }
}

What The Scores Mean

FieldPlain-English meaning
coverage.totalVisitorsAll first-party visitors in range, before lane separation.
coverage.humanVisitorsVisitors eligible for human analysis after bot-classed traffic is suppressed.
coverage.segmentVisitorsHuman visitors that matched the supplied filters, or all human visitors when filters are empty.
coverage.botSuppressedVisitorsNon-human visitors measured separately from the human result.
coverage.matchablePeopleMatched visitors with a first-party identifier useful for internal matching checks.
coverage.hashedIdentifierPeopleMatched visitors with hashed email or phone available.
coverage.consentReadyIdentityPeopleMatched visitors that have consent-safe hashed identifiers available for the API's readiness output.
coverage.coverageScoreMatched human visitors compared with the human visitors in scope.
coverage.confidenceScoreVolume plus specificity; higher means the proof has stronger evidence.
coverage.matchabilityScoreShare of the result with matchable identity.
coverage.identityReadinessScoreShare of the result with enough consent-safe identity signal for the API's readiness output.
coverage.freshnessScoreHow recently matched visitors were seen.

AI/Search Coverage View

Use answerEngine to compare what machines can see with what humans actually use.

function findDiscoveryGaps(proof) {
  const crawled = new Set(proof.answerEngine.topCrawledPages.map((page) => page.path));
  return proof.answerEngine.topHumanPages
    .filter((page) => !crawled.has(page.path))
    .map((page) => ({
      path: page.path,
      action: 'Add structured content, metadata, and edge capture coverage.',
    }));
}

This is useful for content and SEO work: a page with human use but no crawler coverage is a practical improvement target.

Hashed-Identifier Readiness

Use readiness to decide whether the result is strong enough for on-site decisions, reporting, or content planning:

function hasEnoughIdentitySignal(proof) {
  return (
    proof.readiness.identityCoverageReady &&
    proof.coverage.consentReadyIdentityPeople > 0 &&
    proof.coverage.botSuppressedVisitors >= 0
  );
}

If blockedReasons includes no_hashed_identifiers, the result can still be useful for on-site decisions and content planning, but it is not strong enough for identity-backed reporting. If it includes missing_marketing_consent, fix consent capture before relying on identity-style workflows; that reason name mirrors the SDK consent category.

Privacy Notes

See Also