---
title: Fraud Prevention — Ad Fraud
---
# Ad fraud
This is a **scenario guide**. The core integration is unchanged — you load the
[Web SDK](../web-sdk), read a [verdict](../verdict-reference), and pull data from the
[Data API](../data-api) exactly as described in those pages. This page adds the few
specifics that only the ad-fraud scenario needs.
Ad fraud protection filters bot and invalid traffic out of paid campaigns, so
advertisers and ad platforms pay for genuine clicks and impressions — not fake
volume. What makes it different from the other use cases is that **a third party
delivers the visitor**, and both sides need to settle on an independent,
per-click human / bot conclusion.
## Real-time blocking at the source
Fraud Prevention runs inline at the entry point and returns a verdict in real time — it is not an after-the-fact report. Whoever owns the page the visitor lands on can act on that verdict immediately:
- **Advertisers** run the SDK on the landing page: invalid traffic is caught — and optionally challenged — the moment it arrives, before it enters the funnel or distorts reporting.
- **Traffic platforms** run the SDK on their own pre-lander or redirect page: a click is verified *before* it is forwarded, so bots are stopped at the entry and never become delivered traffic.
The only requirement is a page that can run the SDK: a verdict needs browser signals, so a pure server-side 302 has nowhere to collect them — add a lightweight interstitial and the same real-time block applies.
The signed click token and Data API sit on top for reconciliation across both sides; real-time inline blocking is the primary mechanism, and post-hoc API scrubbing is the fallback for traffic with no interstitial.
## The two roles
- **Advertiser** — owns the landing page the visitor arrives on. Runs the Web SDK on
that page to get a verdict for each arriving visit, and pulls verdicts from the
Data API to reconcile what it was billed for.
- **Provider** (traffic / ad source) — delivers the click. It needs to **prove the
quality of what it sent**, so it issues a click token per click and later joins
its delivery report against the independent verdicts.
Both sides authenticate to the [Data API](../data-api) with their own application
credentials and read the same per-click verdict, which is what lets them reconcile
without trusting each other's raw logs.
## End-to-end flow
The sequence below shows how one click travels from the provider's delivery, through
the visitor and the advertiser's landing page, to a single verdict that **both
accounts later read from their own side**.
```mermaid
sequenceDiagram
autonumber
participant P as Provider (account B)
participant V as Visitor
participant A as Advertiser page + SDK
participant F as Fraud Prevention API
P->>P: Sign click_token (bot_kid + secret, carries pkid)
P->>V: Deliver link with ?_ctk=click_token
V->>A: Click → land on advertiser page (URL carries _ctk)
A->>A: SDK reads _ctk from the URL
A->>F: Encrypted POST /bot/verify
(app_key = advertiser, click_token = provider-signed)
F->>F: app_key → advertiser_app_id,
verify pkid → provider_app_id, reach a verdict,
write one row carrying both app_ids
F-->>A: verdict → onVerdict
A->>A: Handle per verdict.action (record / block / challenge)
Note over P,F: Reconciliation (cross-account)
A->>F: GET /v1/bot with advertiser App-Key
F-->>A: Same row (matched on advertiser_app_id)
P->>F: GET /v1/bot with provider App-Key
F-->>P: Same row (matched on provider_app_id)
```
The decisive property: **both app_ids live on the same row.** The advertiser reads it
matched on `advertiser_app_id`, the provider reads it matched on `provider_app_id` —
two different accounts, each querying with its own App-Key, each landing on the same
independent verdict. Neither side has to expose or trust the other's raw logs to
agree on what happened for a given click.
## Click tokens
A **click token** ties a provider's delivered click to the verdict the visit
ultimately receives. It is the per-click identifier that both parties settle on. The
other Fraud Prevention use cases don't need click tokens — they reconcile verdicts
without them.
The flow:
1. **Issue** — the provider obtains a signed click token (one per click) when it
routes a visitor toward the advertiser's landing page.
2. **Carry it on the destination URL** — append the issued token to the landing URL
as a query parameter:
```
https://advertiser.example/lp?click_token=ct_xxxxxxxx
```
3. **Read it on the page** — the [Web SDK](../web-sdk) reads the token from the URL
automatically. If you use a different parameter name, set `tokenParam`:
```js
BotSignal.init({ appKey: 'YOUR_APP_KEY', tokenParam: 'click_token' });
```
4. **Reconcile** — look the click up later via the Data API and join it back to the
provider's delivery report.
::: info
The token is already signed when it is issued to you — you only need to **carry it
through to the landing URL**. There is nothing to sign or compute on your side.
:::
### `tokenParam` option
The Web SDK gains one extra option in this scenario:
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `tokenParam` | string | auto-detected | Name of the URL query parameter that carries the provider click token (e.g. `click_token`). The SDK reads it from the page URL and ties the verdict to that click. Leave unset if you don't use click tokens. |
## Reconciliation & settlement
Once visits carry a click token, both sides settle on the same independent verdicts:
- **Fetch a single click's verdict** — for example to dispute or confirm one click:
```bash
GET /v1/bot/verdict?click_token=ct_xxx
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
```
- **Export per-click rows** — pull a time range and join each row's `click_token`
back to your own click logs:
```bash
GET /v1/bot/export?from=2026-06-01&to=2026-06-30&format=csv
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
```
Each row carries the visit's `click_token` (when present), timestamp, and verdict
fields (`is_bot`, `score`, `level`, `action`).
The advertiser excludes flagged and bot clicks from what it pays for; the provider
reconciles its delivery report against the same conclusions. Because both read the
**same independent verdict**, settlement does not depend on either side's raw logs.
## Next steps
- [Web SDK](../web-sdk) — collect verdicts on the landing page
- [Verdict Reference](../verdict-reference) — every field and how to act on it
- [Data API](../data-api) — pull and reconcile verdicts server-side