Proofs of Engagement

Every visit gets a receipt, and every receipt carries a verdict.

Instead of cookies or cross-site identifiers, AdPriva issues a cryptographic Proof of Engagement for each page view the tag reports. Each proof is scored for how likely it is to come from a real person, and the score and verdict are published with the proof. Anyone can then re-check that the proof exists, what it was judged to be, and where it is anchored.

This means:

  • Publishers can show which share of their traffic looks human, with a receipt for every page view.
  • Buyers and auditors can re-check any single proof and its verdict without an AdPriva login.
  • Nothing that identifies a visitor, a publisher or a site is published.

The view proof

The tag sends one kind of traffic event: view.

  • It is sent once per page load, about one second after the page loads. It carries the aggregate interaction counts collected so far (see AdPriva Tag).
  • The server turns it into an HMAC-signed proof, then scores it, batches it and anchors it.

Web-form consent is recorded separately, as a consent certificate with its own record. See the Consent Certificates quickstart.

Validity score and verdict

Each view proof gets a 0–100 validity score, where higher means more likely to be a person. The score maps to a verdict:

VerdictMeaningScore
humanConsistent with a person using the page70–100
uncertainNot enough evidence either way30–69
abusive_botAutomation that is not declared, such as a headless browser, a browser-automation tool or scripted behaviour0–29
authorized_agentA known automated agent, such as a search crawler or an AI assistant, that proved its identity with a valid signed request (Web Bot Auth, RFC 9421). Declared, verified agents are not fraud, so they get their own verdict instead of being counted as botsGiven by the scoring service only (see below)

The score is not a percentage of certainty. It is a single number that places the event in one of the three bands.

AdPriva also keeps the signals behind each score, for example headless_ua or datacenter_ip. The public record shows only the score and the verdict.

How the verdict is reached: four layers

The four layers run in two places, and they produce two results.

LayerWhat it checksWhere it runs
L0 Self-identificationThe User-Agent header: headless browsers, automation tools, scripts, known crawlers, or no user agent at allIngest server
L1 EnvironmentWhether the client’s IP address is in a datacenter or cloud rangeIngest server
L2 BehaviourWhether the aggregate interaction pattern (dwell, scroll cadence, click timing, pointer speed) and the tag’s automation hints look like a person, using rules plus an anomaly-detection modelAdPriva scoring service
L3 Agent identityWhether the request carries a valid Web Bot Auth signature from a registered keyAdPriva scoring service

The public score and verdict come from L0 and L1. The ingest server computes them when it stores the proof. The score starts at 100, and each matching L0 or L1 signal subtracts a penalty. These two layers never give authorized_agent.

The scoring service runs L2 and L3 after the proof is stored. It returns its own score, verdict and reasons, which AdPriva keeps privately next to the proof. In that result, L3 takes precedence: a valid signature yields authorized_agent, and a forged or expired one yields abusive_bot, whatever the behaviour looks like. The console shows this result in the event details and the Delta report. It does not change the public verdict today. If the scoring service is unavailable, the proof keeps its public verdict and has no second result.

Shadow mode: verdicts are recorded, nothing is blocked

AdPriva currently runs in shadow mode. Every view proof is scored, and the verdict is stored and published, but no proof is blocked, dropped or excluded from a batch because of its verdict. This lets the verdicts be measured against real traffic and reviewed before they are allowed to change anything.

The public explorer reports the current mode (enforcement_mode: "shadow") together with how much of the traffic has been scored. Moving to enforcement is a separate, gated decision, and this page will say so when it happens.

Proof Lifecycle

  1. Trigger: a visitor opens a page, and about one second later the tag sends the view event.
  2. Seal: the server refuses any event whose one-time nonce was already used, then signs the event with an HMAC. The signature is the proof’s public id.
  3. Score: the server gives the proof its public score and verdict (L0 and L1). The scoring service adds its private result (L2 and L3).
  4. Batch and anchor: proofs are grouped into a Merkle tree, and the batch root is anchored on Horizen.
  5. Verify: anyone holding the proof id can look it up, get a Merkle inclusion receipt, and recompute the anchored root offline.

What a public proof looks like

This is the shape returned by GET https://api.adpriva.com/public/proofs/{proof_id} (shortened; the full response, including the Merkle receipt, is in the API Reference):

{
  "proof_id": "dcea8f8ef8f162c6b7269dd4d1ad24708f6358cbd6fa6d48dd528ab0e5e29b08",
  "type": "view",
  "status": "verified",
  "created_at": "2026-10-16T20:39:14Z",
  "merkle_root": "0x05f559a4dbb3c8f4cce6434b3e933468cc23a1c5bba2c479275d3349254d5e5c",
  "zkverify_status": "NOT_APPLICABLE",
  "horizen_status": "CONFIRMED",
  "validity_score": 100,
  "verdict": "human"
}

The proof id is an opaque signature. The record carries no publisher, site, IP address, session or raw event data.

Fraud Resistance

  • Replay prevention: each event carries a one-time nonce, and an event whose nonce was already used is refused.
  • Tamper-evidence: the HMAC seal breaks if any sealed field changes, and the anchored batch root fixes which proofs are in each batch (not their score or verdict).
  • Visible verdicts: automated traffic still receives a proof, marked abusive_bot, so the record shows what was flagged instead of silently dropping it.
  • Independent re-checking: the Merkle receipt lets anyone confirm a proof’s batch membership against the on-chain root, without trusting the API’s status field.

See Fraud & Threat Model for what these protections do not cover.