> ## 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.

# Phone Verification Warnings

> Reference for every Phone Verification risk code: HIGH_RISK_PHONE_NUMBER, VOIP_NUMBER_DETECTED, DISPOSABLE_NUMBER_DETECTED, blocklist, and OTP caps.

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>;

The Phone Verification feature emits **warnings** whenever a risk signal fires on the OTP flow or the number itself: a known bad number, a VoIP or disposable line, an OTP attempt cap, or a hit against another user's session. This page lists every code, what triggers it, and how to configure the workflow response.

## Overview

Warnings are tagged with feature `PHONE`. They appear under `phone_verifications[].warnings[]` in `GET /v3/session/{sessionId}/decision/` and follow the standard [warning object](/reference/data-models#warning-object) shape (`feature`, `risk`, `additional_data`, `log_type`, `short_description`, `long_description`, `node_id`). Each warning is also routed into the per-session log so it surfaces in the Business Console under the Phone section.

Some warnings are **hard auto-decline** triggers — they always set the feature status to `Declined`. Others map to a **configurable action** (`DECLINE`, `REVIEW`, or `NO_ACTION`) defined in your workflow settings, so the same risk code may decline one workflow while only flagging another.

## Auto-decline conditions

The following warnings always decline the Phone Verification step regardless of configuration, and always carry `log_type: "error"`:

* `VERIFICATION_CODE_ATTEMPTS_EXCEEDED` — the user exhausted an OTP attempt cap: by default 2 wrong code submissions (`phone_max_check_attempts`) or more than 2 OTP sends (`phone_max_retries`), both tunable per workflow node. The standalone phone API allows 3 code attempts.
* `PHONE_NUMBER_IN_BLOCKLIST` — the number is in your blocklist managed via the [Lists API](/management-api/lists/overview), or it matches a blocklisted session.
* `HIGH_RISK_PHONE_NUMBER` — the delivery provider's anti-fraud layer refused to send the OTP (`Blocked` send). The blocking reason (`repeated_attempts`, `suspicious`, or `spam`) is returned in `additional_data.blocked_reason`.

## Configurable risks

Each of the following risks maps to a workflow setting you can configure to `DECLINE`, `REVIEW`, or `NO_ACTION` (the default — the warning is recorded without affecting the status). The configured action also sets the warning's `log_type`: `DECLINE` → `error`, `REVIEW` → `warning`, `NO_ACTION` → `information`.

| Setting                          | Risk code                    | Fires when                                                                                                                  |
| -------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `voip_number_action`             | `VOIP_NUMBER_DETECTED`       | The carrier resolves to a `voip`, `isp`, or `vpn` line type.                                                                |
| `disposable_number_action`       | `DISPOSABLE_NUMBER_DETECTED` | The lookup flags the number as a temporary / burner provider.                                                               |
| `duplicated_phone_number_action` | `DUPLICATED_PHONE_NUMBER`    | The number matches another session of any status belonging to a different end-user. Skipped when the number is allowlisted. |

Duplicate detection runs within your application across KYC, KYB, and standalone API phone verifications, and groups sessions by `vendor_data` — sessions sharing the same `vendor_data` are treated as one end-user and are excluded from match results. Leave `vendor_data` empty and every session is treated as a distinct user.

## Verification attempt limits

The Phone Verification feature applies a hard cap on OTP attempts to prevent abuse:

* **Code-entry attempts** — Default **2** wrong submissions (`phone_max_check_attempts`) before the step finalizes as `Declined` with `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`. The standalone phone API allows **3** code attempts per verification.
* **Send attempts** — Default **2** sends in total (`phone_max_retries`): the initial send plus one resend. A further send request finalizes the step and raises `VERIFICATION_CODE_ATTEMPTS_EXCEEDED`.
* **OTP validity** — A pending OTP is valid for **5 minutes from the first send**; retries do not extend the window.

Both caps are tunable per workflow node. Independently of these caps, the delivery provider's anti-fraud layer can block a send outright (reason `repeated_attempts`, `suspicious`, or `spam`) — that raises `HIGH_RISK_PHONE_NUMBER`, not the attempts warning.

## Warnings produced

| Tag                                   | `log_type`                               | Description                                                                                                                                                                                                                                                           |
| ------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VERIFICATION_CODE_ATTEMPTS_EXCEEDED` | `error` (always)                         | The user exceeded the OTP code-entry or send cap. Auto-declines.                                                                                                                                                                                                      |
| `HIGH_RISK_PHONE_NUMBER`              | `error` (always)                         | The delivery provider blocked the OTP send. Auto-declines. `additional_data`: `{ blocked_reason }`.                                                                                                                                                                   |
| `PHONE_NUMBER_IN_BLOCKLIST`           | `error` (always)                         | The number is in a blocklist created via the [Lists API](/management-api/lists/overview), or matches a blocklisted session. Auto-declines. `additional_data`: `{ blocklisted_session_id, blocklisted_session_number, api_service }` (all `null` for a pure list hit). |
| `PHONE_NUMBER_IN_ALLOWLIST`           | `information`                            | The number matched other sessions but is in an allowlist created via the [Lists API](/management-api/lists/overview), so duplicate-phone actions are skipped. `additional_data`: `{ phone_number }`.                                                                  |
| `DISPOSABLE_NUMBER_DETECTED`          | Follows `disposable_number_action`       | The number is a temporary / burner provider often used to evade traceability.                                                                                                                                                                                         |
| `VOIP_NUMBER_DETECTED`                | Follows `voip_number_action`             | The number resolves to a `voip`, `isp`, or `vpn` line rather than a standard mobile or fixed line.                                                                                                                                                                    |
| `DUPLICATED_PHONE_NUMBER`             | Follows `duplicated_phone_number_action` | The number matches another session — of any status — belonging to a different end-user (grouped by `vendor_data`). `additional_data`: `{ duplicated_session_id, duplicated_session_number, api_service }`.                                                            |

## Cross-session matches

When a number is detected on other sessions or on your blocklist, those hits also appear under `phone_verifications[].matches[]` (capped at 5 entries). Each match carries `session_id`, `session_number`, `vendor_data`, `verification_date`, `phone_number`, `status`, `is_blocklisted`, `api_service`, and a `source` of `session` (another session with the same number, any status) or `list_entry` (a synthetic blocklist hit prepended to the array). If the current number is on your phone allowlist, Didit keeps the match evidence but emits `PHONE_NUMBER_IN_ALLOWLIST` instead of applying the duplicate-phone action. See the [Phone Verification report](/core-technology/phone-verification/report-phone-verification#cross-session-matches) for the full match schema.

## Example

A blocked send (`HIGH_RISK_PHONE_NUMBER`) on a VoIP number, with `voip_number_action` configured to `REVIEW`:

```json theme={null}
{
  "warnings": [
    {
      "feature": "PHONE",
      "risk": "HIGH_RISK_PHONE_NUMBER",
      "additional_data": { "blocked_reason": "suspicious" },
      "log_type": "error",
      "short_description": "High risk phone number",
      "long_description": "The system detected that the phone number is a high risk phone number, which is not allowed.",
      "node_id": "feature_phone_1"
    },
    {
      "feature": "PHONE",
      "risk": "VOIP_NUMBER_DETECTED",
      "additional_data": null,
      "log_type": "warning",
      "short_description": "VoIP number detected",
      "long_description": "The system detected that the phone number is a VoIP number, which is not allowed.",
      "node_id": "feature_phone_1"
    }
  ]
}
```

## Warning types

Each risk is assigned a severity based on your application's configuration. Severities fall into three categories:

<WarningTypes />

## Related

* [Phone Verification overview](/core-technology/phone-verification/overview) — feature behavior, pricing, supported channels.
* [Phone Verification report](/core-technology/phone-verification/report-phone-verification) — full response shape including `lifecycle[]` and `matches[]`.
* [Data models — phone verification](/reference/data-models#phone-verification) — canonical field-by-field schema.
* [Management API — Lists](/management-api/lists/overview) — manage the phone blocklist used by `PHONE_NUMBER_IN_BLOCKLIST`.
