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

# Categories and actions

> What each classification tells you, and a default action to start from at a decision point.

Each lookup returns one `classification` and the `is_*` flags that go with it.
This page suggests what to do with each one at a decision point such as a
sign-up, a login or a checkout:

* **Allow**: let it through.
* **Challenge**: ask for a step-up check the person can pass, such as a
  one-time code.
* **Review**: hold it, or let it through flagged, for a person or your next
  signal to decide.
* **Block**: refuse it.

These are starting points, not guarantees. A lookup classifies the IP address,
not the person behind it, so test any rule on your own traffic before you
enforce it, and adjust it to what a wrong call costs you.

The sample in the [lookups guide](https://vpn-docs.wxapros.com/guides/lookups.md#single-ip) is the strict
version: it blocks every VPN except a company VPN, every proxy, every
residential proxy hit and Tor. The defaults here block Tor too, but they are
gentler on VPNs, proxies and residential proxy hits, and they add a review for
hosting addresses.

## By category

| Result | What it tells you | Suggested default |
| - | - | - |
| `is_tor` | A public Tor exit relay: the traffic could come from anyone using Tor. | Block, or challenge if you serve people who use Tor for privacy |
| `is_residential_proxy` | An address with a residential proxy label. Its `risk_score` follows how recently we saw it: current 95–100, recent 75–85, stale 45–60, frozen 25–35. | Block above a `risk_score` of 60; challenge at 60 or below |
| `is_proxy` | A proxy server other than a residential proxy exit, such as one on hosting space or on a public open-proxy list (`is_open_proxy`). | Block; challenge `suspected_proxy` and `mobile_proxy` |
| `is_vpn` without `is_corporate_vpn` | A confirmed VPN endpoint, such as a consumer VPN service's server. Its location is the server's, not the person's. | Challenge; in KYC onboarding, review |
| `is_corporate_vpn` | A company VPN appliance. `is_vpn` is true as well. | Allow |
| `is_relay` | A privacy relay, such as iCloud Private Relay. | Allow |
| `is_hosting` | A datacenter or cloud provider's address. Major search crawlers run from their operators' cloud ranges, so they return `hosting` too. | Review; allow verified search crawlers first |
| `is_cdn` | A content delivery network's edge address. | Allow |
| `suspected_vpn` or `vpn_suspect` | Advisory: traffic patterns resemble a VPN, but we lack confirmed protocol evidence. No flag is set. | Allow, or review above a `risk_score` of 60; don't block on it alone |
| `unknown` | We hold no label for this IP. Treat it as no data, not as clean. | Decide on your other signals |
| Any other `classification` | A label without a row here, with no flag set. | Decide on `risk_score`: block above 60, otherwise review |

Two proxy labels get a softer default. `suspected_proxy` is proxy evidence that
falls short of confirmation, and `mobile_proxy` (not assigned since 2026-05-02)
sits on a mobile carrier's addresses, which many people share. Both set
`is_proxy`.

To check a search crawler, look its address up in the operator's published IP
list, or run a reverse-then-forward DNS lookup. If many of your lookups return
`cdn`, check that you look up the visitor's address rather than your own CDN's.

The same defaults in code. It reads only fields that every plan returns:

```python theme={null}
ALLOW_LABELS = ("relay", "cdn", "suspected_vpn", "vpn_suspect", "unknown")

def default_action(r):
    """A starting point: test it on your own traffic before you enforce it."""
    if r["is_tor"]:
        return "block"
    if r["is_residential_proxy"]:
        return "block" if r["risk_score"] > 60 else "challenge"
    if r["is_proxy"]:
        if r["classification"] in ("suspected_proxy", "mobile_proxy"):
            return "challenge"
        return "block"
    if r["is_vpn"]:
        return "allow" if r["is_corporate_vpn"] else "challenge"
    if r["is_hosting"]:
        return "review"  # allow verified search crawlers before this check
    if r["classification"] not in ALLOW_LABELS:
        # Any other classification: decide on risk_score.
        return "block" if r["risk_score"] > 60 else "review"
    # relay, cdn, suspected_vpn, vpn_suspect and unknown: your other signals
    # decide, but a risk_score above 60 is held for review.
    return "review" if r["risk_score"] > 60 else "allow"
```

## Shared addresses

On a mobile carrier, a satellite network or a secure web gateway, many people
share one address, so a proxy sighting there says little about the person in
front of you. A residential proxy hit on one of these networks that we list
scores at most 60, however recent, and carries the `SHARED_ADDRESS_NETWORK`
reason, so the default above challenges it rather than blocking it. A VPN,
proxy, threat-list or hosting match on the same address keeps the score it
would have on its own, so a hit on a listed network can still score above 60;
the reason is then left out.

The list does not yet cover every such network, and `infra_type` does not mark
mobile networks, so the reason, not the network's type, tells you the cap
applied. On a shared network we don't list, a residential hit keeps its full
score: if you know the network is shared, challenge or review the hit rather
than block it.

On the networks that Zscaler, Netskope and Cisco Umbrella run themselves, an
address with no range or observation of its own answers `unknown` with a
`risk_score` of 0 (see [Tiers](https://vpn-docs.wxapros.com/tiers.md#fields-populated-by-evidence-class)).

## Where location rules apply

For content licensing or regional pricing, ask for a second location signal
when `is_vpn`, `is_relay` or `is_tor` is true. That includes corporate VPNs and
privacy relays, which the defaults above allow.

## Think a result is wrong?

See [Report a result you think is wrong](https://vpn-docs.wxapros.com/guides/lookups.md#report-a-result-you-think-is-wrong).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.