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

# Device & IP analysis warnings

> Every Device & IP analysis warning code: VPN/Tor, country mismatch, blocklists, duplicate IP/device, recovered devices, and expected-IP mismatch.

export const WarningTypes = () => <div style={{
  display: "flex",
  flexDirection: "column",
  gap: "12px",
  margin: "16px 0"
}}>
    {WARNING_TYPES.map(w => <div key={w.type} style={{
  display: "flex",
  alignItems: "flex-start",
  gap: "12px",
  padding: "12px 16px",
  borderRadius: "8px",
  background: w.bg
}}>
        <span style={{
  fontSize: "16px",
  lineHeight: "24px"
}}>{w.icon}</span>
        <div>
          <div style={{
  fontWeight: 600,
  color: w.color,
  marginBottom: "2px"
}}>
            <code style={{
  background: "transparent",
  color: w.color,
  fontWeight: 600,
  padding: 0
}}>
              {w.type}
            </code>
          </div>
          <div style={{
  fontSize: "14px",
  lineHeight: "20px",
  color: "#374151"
}}>
            {w.description}
          </div>
        </div>
      </div>)}
  </div>;

## Overview

Device & IP analysis emits warning tags on `ip_analyses[].warnings[]` whenever it detects suspicious device, network, or location behavior. Six tags have a configurable action (Decline / Review / No action) per workflow node; the two blocklist tags always force `Declined`, and the two allowlist tags are always informational. Each warning's `log_type` mirrors the action that applied: `error` (Decline), `warning` (Review), or `information` (No action). Each risk fires at most once per session.

Every risk below is verified against the live decision pipeline; warnings carry the feature tag `LOCATION` and the standard [warning object](/reference/data-models#warning-object) shape.

The device fingerprint warnings are intentionally split into exact and recovered signals:

* `DUPLICATED_DEVICE_FINGERPRINT` means the same deterministic device identity (`match_source` `persistent_id` or `legacy_fp`) was reused across sessions with different `vendor_data` values.
* `DEVICE_RECOVERED_HIGH_CONFIDENCE` means v2 fingerprint recovery matched the session to a previously seen device **after** the persistent ID changed (storage cleared, incognito mode, app reinstall). The warning only appears when the match passes the high-confidence similarity threshold and hard gates.

<Frame>
  <img src="https://mintcdn.com/didit-0f962782/z6T2GHM4Zh-iSj-K/images/ip-analysis-warning-example.png?fit=max&auto=format&n=z6T2GHM4Zh-iSj-K&q=85&s=370f87af45a6138ebd7454abe9131665" alt="Didit Device and IP Analysis warning example with device, VPN, proxy and location-mismatch alerts" width="1670" height="860" data-path="images/ip-analysis-warning-example.png" />
</Frame>

## Warnings produced

| Risk                                                   | Cause                                                                                                                                                                                                                                                                                                                                                            | `log_type`                | Affects status                                                   | Recommended remediation                                                                                                              |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `PRIVATE_NETWORK_DETECTED`                             | `is_vpn_or_tor` was `true` — the session was opened via VPN, proxy, or Tor exit node. `additional_data` is `null`.                                                                                                                                                                                                                                               | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Decide whether your risk tolerance allows masked traffic. If you accept it, leave on No action.                                      |
| `COUNTRY_FROM_DOCUMENT_DOES_NOT_MATCH_COUNTRY_FROM_IP` | The ISO country of the ID document differs from the country derived from the IP address. `additional_data` carries both ISO-3 codes: `document_country_code`, `ip_country_code`.                                                                                                                                                                                 | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Review when the user travels legitimately. Decline when paired with VPN or duplicate-device signals.                                 |
| `EXPECTED_IP_ADDRESS_MISMATCH`                         | The session was created with an expected IP address, and the live IP differs. `additional_data` carries `expected_ip_address` and `actual_ip_address`.                                                                                                                                                                                                           | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Use when you pre-pin the IP at session creation. Refuse the session or step up auth on mismatch.                                     |
| `IP_ADDRESS_IN_BLOCKLIST`                              | The session's IP address matches an entry in the application's IP blocklist. `additional_data`: `ip_address`.                                                                                                                                                                                                                                                    | always `error`            | Forces `Declined`.                                               | Reject the session. Audit the blocklist source for false positives if needed.                                                        |
| `DEVICE_FINGERPRINT_IN_BLOCKLIST`                      | The session's device fingerprint matches an entry in the device blocklist. `additional_data`: `device_fingerprint`.                                                                                                                                                                                                                                              | always `error`            | Forces `Declined`.                                               | Reject the session.                                                                                                                  |
| `IP_ADDRESS_IN_ALLOWLIST`                              | The session's IP had duplicate matches but is on the application's IP allowlist, so the duplicate-IP warning was skipped. `additional_data`: `ip_address`.                                                                                                                                                                                                       | always `information`      | None.                                                            | Use for trusted corporate NATs, QA networks, or known shared access points.                                                          |
| `DEVICE_FINGERPRINT_IN_ALLOWLIST`                      | The session's device fingerprint had deterministic duplicate matches but is on the device allowlist, so the duplicate-device warning was skipped. `additional_data`: `device_fingerprint`.                                                                                                                                                                       | always `information`      | None.                                                            | Use only for trusted shared devices. Blocklists still take priority.                                                                 |
| `DUPLICATED_IP_ADDRESS`                                | The same IP was used in another session with a different `vendor_data`. `additional_data`: `duplicated_session_id`, `duplicated_session_number`, `api_service`. Skipped when the IP is allowlisted.                                                                                                                                                              | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Common on corporate or shared NAT. Pair with other signals (duplicate device, country mismatch) before declining.                    |
| `DUPLICATED_DEVICE_FINGERPRINT`                        | The same deterministic device identity was reused across sessions with different `vendor_data`. `additional_data`: `duplicated_session_id`, `duplicated_session_number`, `api_service`, `match_source` (`persistent_id` or `legacy_fp`). Skipped when the fingerprint is allowlisted.                                                                            | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Strong evidence of multi-account abuse. Review or decline depending on fraud tolerance.                                              |
| `DEVICE_RECOVERED_HIGH_CONFIDENCE`                     | The v2 device recovery model matched the session to a previously seen device with high confidence, even after the persistent ID changed. `additional_data`: `duplicated_session_id`, `duplicated_session_number`, `api_service`, `match_source` (`recovered_high`), `recovery_similarity`, `recovery_match_device_uuid`. Not suppressed by the device allowlist. | mirrors configured action | Configurable: Decline / Review / No action (default: No action). | Start with Review, measure your false-positive rate, then tighten the action. Strong signal for storage-reset or incognito attempts. |

When the recovered device has no eligible sibling sessions yet (so nothing lands in `matches[]`), `DEVICE_RECOVERED_HIGH_CONFIDENCE` still fires with a fallback `additional_data` shape: `recovery_match_device_uuid`, `recovery_match_similarity`, `recovery_match_band`, `recovery_gate_reason`.

## Configurable settings

Per-node workflow controls (also configurable globally on the application's verification settings; every action defaults to **No action**):

| Setting                       | Drives                                                 | Default   |
| ----------------------------- | ------------------------------------------------------ | --------- |
| `vpn_detection_action`        | `PRIVATE_NETWORK_DETECTED`                             | No action |
| `ip_mismatch_action`          | `COUNTRY_FROM_DOCUMENT_DOES_NOT_MATCH_COUNTRY_FROM_IP` | No action |
| `expected_ip_mismatch_action` | `EXPECTED_IP_ADDRESS_MISMATCH`                         | No action |
| `duplicated_ip_action`        | `DUPLICATED_IP_ADDRESS`                                | No action |
| `duplicated_device_action`    | `DUPLICATED_DEVICE_FINGERPRINT`                        | No action |
| `recovered_device_action`     | `DEVICE_RECOVERED_HIGH_CONFIDENCE`                     | No action |

IP and device allowlists suppress only the exact duplicate-IP or deterministic duplicate-device warning for the allowlisted value. They do not suppress VPN/proxy, country mismatch, expected-IP mismatch, recovered-device, or blocklist warnings.

Recovered-device warnings are useful for detecting fraud rings that rotate accounts and sessions from the same hardware, but Didit is conservative by design — the system prefers missing some duplicate users over merging unrelated devices when the evidence is not strong enough. **Recommended rollout**: start with Review, watch the volume for two weeks, then tighten the action if your false-positive rate is low.

## Duplicate device vs. recovered device

Treat the two warnings differently — they trigger on different evidence:

| Warning                            | Trigger                                                                                            | Typical meaning                                                                                                                    | Recommended first action                                             |
| ---------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `DUPLICATED_DEVICE_FINGERPRINT`    | Exact persistent device identity (`persistent_id`) or legacy fingerprint hash (`legacy_fp`) match. | Strong evidence that the same device appears across different users.                                                               | Review or Decline, depending on your fraud tolerance.                |
| `DEVICE_RECOVERED_HIGH_CONFIDENCE` | High-confidence v2 recovery (`recovered_high`) after storage, session, or app identity changed.    | Strong signal for incognito / storage-reset / app-reinstall attempts. Intentionally separated so you can monitor it independently. | Review first, then tighten after measuring your false-positive rate. |

## Cross-session matches

When the same IP address, exact device identity, or recovered device is detected across sessions belonging to **different users**, Didit records these as `matches[]` on `ip_analyses[]`. Sessions are grouped by `vendor_data`: sessions with the same `vendor_data` are treated as the same user and excluded from matches. Without `vendor_data`, every session is treated as a unique user and all potential duplicates are surfaced — we strongly recommend always providing `vendor_data` to reduce noise.

Each match includes:

* `session_id`, `session_number`, `vendor_data`, and `verification_date` of the matching session, plus its lifecycle `status` and `api_service` (`null` for workflow sessions); `source` is always `session`
* `match_type` — `ip_address` or `device_fingerprint`
* `match_source` — `ip_address`, `persistent_id`, `legacy_fp`, or `recovered_high`
* `matched_value` — the shared IP, the device identifier, or (for recovered matches) the recovered device UUID
* `confidence` (0–1, `1 - P(false positive)`) and `match_mode` (`deterministic` / `probabilistic` / `co_occurrence`)
* `device_info` — `device_brand`, `device_model`, `browser_family`, `os_family`, `platform`, `device_fingerprint`
* `location_info` — `ip_address`, `ip_country`, `ip_country_code`, `ip_state`, `ip_city`, `is_vpn_or_tor`, `is_data_center`
* Recovered-device extras when `match_source` is `recovered_high`: `recovery_similarity`, `tls_ja4_corroborated`, `recovery_gate_reason`

IP matches and device matches are capped at 5 each (at most 10 entries). See the [report page](/core-technology/ip-analysis/report-ip-analysis#match-semantics) for the full match schema and confidence model.

## Examples

### VPN + country mismatch (configured to Review)

```json theme={null}
{
  "warnings": [
    {
      "feature": "LOCATION",
      "risk": "PRIVATE_NETWORK_DETECTED",
      "additional_data": null,
      "log_type": "warning",
      "short_description": "Private network (VPN/Tor) detected",
      "long_description": "The system detected that the user tried to use a private network (VPN/TOR) to complete the verification process.",
      "node_id": "ip-1"
    },
    {
      "feature": "LOCATION",
      "risk": "COUNTRY_FROM_DOCUMENT_DOES_NOT_MATCH_COUNTRY_FROM_IP",
      "additional_data": { "document_country_code": "ESP", "ip_country_code": "NLD" },
      "log_type": "warning",
      "short_description": "Document country does not match IP country",
      "long_description": "The country from the document does not match the country from the IP address, suggesting a potential mismatch between the document and the user's location.",
      "node_id": "ip-1"
    }
  ]
}
```

### Blocklist hit (forces decline)

```json theme={null}
{
  "warnings": [
    {
      "feature": "LOCATION",
      "risk": "IP_ADDRESS_IN_BLOCKLIST",
      "additional_data": { "ip_address": "45.61.20.5" },
      "log_type": "error",
      "short_description": "IP address in blocklist",
      "long_description": "The IP address used for this session was found in the application's IP blocklist, indicating a known suspicious or forbidden origin.",
      "node_id": "ip-1"
    }
  ]
}
```

### Recovered-device high confidence (configured to Review)

```json theme={null}
{
  "warnings": [
    {
      "feature": "LOCATION",
      "risk": "DEVICE_RECOVERED_HIGH_CONFIDENCE",
      "additional_data": {
        "duplicated_session_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
        "duplicated_session_number": 5101,
        "api_service": null,
        "match_source": "recovered_high",
        "recovery_similarity": 0.9874,
        "recovery_match_device_uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
      },
      "log_type": "warning",
      "short_description": "Device matches a previously seen device with high confidence",
      "long_description": "A previously seen device matched this session with high confidence through v2 fuzzy fingerprint recovery after passing strict hard gates, indicating the same physical device under a fresh persistent_id.",
      "node_id": "ip-1"
    }
  ]
}
```

## Related

* [Device & IP analysis report](/core-technology/ip-analysis/report-ip-analysis) — full response shape and status semantics
* [Device & IP analysis overview](/core-technology/ip-analysis/overview) — what each block measures
* [Data models — IP analysis](/reference/data-models#ip-analysis) — canonical schema
* [Webhooks](/integration/webhooks) — `status.updated` carries IP analysis warnings

### Warning types

<WarningTypes />
