Skip to main content
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 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, 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: DECLINEerror, REVIEWwarning, NO_ACTIONinformation.
SettingRisk codeFires when
voip_number_actionVOIP_NUMBER_DETECTEDThe carrier resolves to a voip, isp, or vpn line type.
disposable_number_actionDISPOSABLE_NUMBER_DETECTEDThe lookup flags the number as a temporary / burner provider.
duplicated_phone_number_actionDUPLICATED_PHONE_NUMBERThe 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

Taglog_typeDescription
VERIFICATION_CODE_ATTEMPTS_EXCEEDEDerror (always)The user exceeded the OTP code-entry or send cap. Auto-declines.
HIGH_RISK_PHONE_NUMBERerror (always)The delivery provider blocked the OTP send. Auto-declines. additional_data: { blocked_reason }.
PHONE_NUMBER_IN_BLOCKLISTerror (always)The number is in a blocklist created via the Lists API, 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_ALLOWLISTinformationThe number matched other sessions but is in an allowlist created via the Lists API, so duplicate-phone actions are skipped. additional_data: { phone_number }.
DISPOSABLE_NUMBER_DETECTEDFollows disposable_number_actionThe number is a temporary / burner provider often used to evade traceability.
VOIP_NUMBER_DETECTEDFollows voip_number_actionThe number resolves to a voip, isp, or vpn line rather than a standard mobile or fixed line.
DUPLICATED_PHONE_NUMBERFollows duplicated_phone_number_actionThe 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 for the full match schema.

Example

A blocked send (HIGH_RISK_PHONE_NUMBER) on a VoIP number, with voip_number_action configured to REVIEW:
{
  "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: