关于 didit-phone-verification
面向需要手机号验证和风险检查的应用,介绍 Didit API 的独立接口。涵盖验证码发送与校验,以及手机号风险信号查询。
主要功能
- 01通过可用渠道发送一次性验证码
- 02校验验证码并返回手机号报告
- 03无需发送验证码即可评估手机号风险
- 04检查运营商、线路类型和欺诈信号
适用场景
- 01在注册时确认用户手机号
- 02识别一次性号码、VoIP 或重复号码
- 03在账户操作前进行手机号风险检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ didit-protocol/ skills --skill didit-phone-verification安装后在 Claude Code 中输入/didit-phone-verification确认已加载。
附带文件scripts/verify_phone.py
详细介绍
Didit Phone Verification API
概览
三个独立端点:
- 发送一次性验证码到电话号码:
POST /v3/phone/send/ - 验证用户输入的验证码并获取电话报告:
POST /v3/phone/check/ - 风险评估(不发送验证码):通过一次调用评估号码:
POST /v3/phone/risk/
关键限制(发送 + 验证):
- 电话号码必须采用 E.164 格式(例如
+14155552671)。系统会验证并规范化号码;无效号码会返回400。 - 验证操作会根据你的应用和
phone_number查找验证记录。它不接受request_id。 - 待处理的验证自首次发送起有效 5 分钟。重试不会延长有效期。
- 每个待处理的验证可重试一次:在有效期内,对同一号码进行第二次发送会返回
"Retry",并使用相同的request_id(总计发送 2 次)。之后再次发送会使用新的request_id开始新的验证。 - 每次验证可尝试输入验证码 3 次。第 1 次和第 2 次输入错误验证码会返回
"Failed";第 3 次输入错误验证码会将验证结果设为"Declined"(VERIFICATION_CODE_ATTEMPTS_EXCEEDED)。 code必须为 4-8 位数字(^[0-9]{4,8}$)。其他输入会返回400,且不消耗尝试次数。- 防滥用限制:每个电话号码每小时最多 4 次发送尝试(
429)。 - 没有待处理的验证(从未发送、已完成、发送时被阻止或已超过 5 分钟):验证操作会返回 HTTP
200,并包含status: "Expired or Not Found"。 - 在你的组织首次充值前,电话验证会一直处于禁用状态(
403)。 - 每次新验证都会存储为 API 会话:
request_id可用于GET /v3/session/{sessionId}/decision/,会显示在 Business Console 中,并会在验证进度更新时触发status.updatedwebhook。
发送渠道: options.preferred_channel 可以是 whatsapp(默认)、sms、telegram、voice、rcs、viber 或 zalo(可用性因国家/地区而异)。如果该号码无法使用首选渠道,系统会回退到 SMS,且发送仍会报告 "Success";实际使用的渠道会显示在 phone.verification_method 和 phone.lifecycle 的发送事件中(details.actual_channel)。
品牌定制: SMS 验证码会使用你的应用的 SMS 发送方名称(在 Business Console 中设置),无需额外费用。options.sms_sender_name 可使用其他名称发送单个验证码;如果未设置发送方名称,options.use_white_label_customization: true 会使用你的白标公开名称。WhatsApp 验证码使用固定消息,不支持品牌定制。
功能: 运营商名称和线路类型、VoIP/虚拟号码和一次性号码、你的会话中的重复号码(matches[])、你的电话黑名单/白名单、发送时的反欺诈拦截、可选的社交足迹(enable_social)、欺诈信号。
沙盒: 沙盒 API 密钥会跳过发送、计费和持久化。发送会返回静态的 "Success" 和随机 request_id;验证时使用验证码 123456(任何格式正确的验证码都会返回简化的 "Approved" 载荷,其中包含扁平的 phone 对象)。风险评估会返回确定性的已批准数据。格式错误的输入仍会返回 400。
定价: Phone Verification 每条发送的消息收费 $0.04 + 运营商费用,费用按目的地国家/地区和实际发送渠道计算(价格表);除 "Blocked" 外,每次发送都会计费,包括 "Retry";验证免费(可选的社交附加功能除外);独立调用没有免费额度。POST /v3/phone/risk/ 没有公布标价(文档称其价格低于 Phone Verification);社交足迹附加功能(enable_social)也没有公布标价(在端点价格基础上按请求计费;查询无法完成时不计费)。这些标价适用至 2026 年 11 月 1 日,届时 Didit 将调整定价,每月 500 次免费验证将改为每月 $10 的免费额度。请访问 didit.me/pricing 查看当前价格。
API 参考:发送验证码 | 校验验证码 | 手机号风险
**功能指南:**https://docs.didit.me/core-technology/phone-verification/overview
身份验证
每个请求都需要 x-api-key 标头。请使用 Didit Business Console 中 Developers > API keys 下的应用 API 密钥,或通过编程方式创建账户和密钥(参见“开始使用”)。具有自定义访问权限的命名密钥需要 Sessions 写入权限才能运行独立检查;仅限部分工作流或仅限已批准会话的密钥无法运行这些检查。
开始使用(还没有账户?)
如果你没有 Didit API 密钥,只需通过 2 次 API 调用即可创建:
- **注册:**使用
{"email": "[email protected]", "password": "MyStr0ng!Pass"}调用POST https://apx.didit.me/auth/v2/programmatic/register/ - 查看电子邮件,获取 6 位验证码
- **验证:**使用
{"email": "[email protected]", "code": "A3K9F2"}调用POST https://apx.didit.me/auth/v2/programmatic/verify-email/。密钥位于响应中的application.api_key。
**添加额度:**调用 GET /v3/billing/balance/ 查看余额;调用 POST /v3/billing/top-up/ 并传入 {"amount_in_dollars": 50},获取 Stripe 结账链接。
如需全面管理平台(工作流、会话、用户、账单),请参阅 didit-verification-management skill。
第 1 步:发送手机验证码
POST https://verification.didit.me/v3/phone/send/
标头
| 标头 | 值 | 必填 |
|---|---|---|
x-api-key | 你的 API 密钥 | 是 |
Content-Type | application/json | 是 |
请求体(JSON)
| 参数 | 类型 | 必填 | 默认值 | 限制 | 说明 |
|---|---|---|---|---|---|
phone_number | string | 是 | - | E.164,最多 20 位 | 手机号码(例如 +14155552671) |
options.code_size | integer | 否 | 6 | 4-8 | 验证码位数 |
options.locale | string | 否 | - | 最多 5 个字符,BCP-47(^[a-z]{2,3}(-[A-Z]{2,3})?$) | 短信语言,例如 en-US、fr、es |
options.preferred_channel | string | 否 | "whatsapp" | whatsapp、sms、telegram、voice、rcs、viber、zalo | 首选渠道;不可用时回退到 SMS |
options.sms_sender_name | string or null | 否 | 你的应用的 SMS 发件人名称 | 1-30 个字符:字母、数字、空格和 ' & - !;不能仅包含数字,且只能使用单个空格 | 此名称会显示在该验证码短信中(“123456 is your verification code for Example Bank”)。无效值会返回 400,且不会发送验证码。每个应用每天(UTC)最多可开始使用两个新名称;超过后将使用应用名称。免费 |
options.use_white_label_customization | boolean | 否 | false | - | 未设置 SMS 发件人名称时,使用你的白标公开名称作为 SMS 签名 |
signals.ip | string | 否 | - | IPv4 或 IPv6 | 用于欺诈检测的最终用户 IP |
signals.device_id | string | 否 | - | 最多 255 个字符 | Android ANDROID_ID、iOS identifierForVendor |
signals.device_platform | string | 否 | - | android、ios、ipados、tvos、web | 设备平台 |
signals.device_model | string | 否 | - | 最多 255 个字符 | 例如 iPhone17,2 |
signals.os_version | string | 否 | - | 最多 64 个字符 | 例如 18.0.1 |
signals.app_version | string | 否 | - | 最多 64 个字符 | 例如 1.2.34 |
signals.user_agent | string | 否 | - | 最多 512 个字符 | 浏览器或应用的 user agent |
vendor_data | string | 否 | - | - | 你的用户 ID。此数据会存储在会话中(从首次发送开始),并在发送、校验和 Webhook 中返回 |
metadata | object | 否 | null | JSON 对象 | 任意格式的数据,会存储在会话中并在响应中返回 |
示例
import requests
response = requests.post(
"https://verification.didit.me/v3/phone/send/",
headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={
"phone_number": "+14155552671",
"options": {"preferred_channel": "sms", "code_size": 6, "sms_sender_name": "Example Bank"},
"signals": {"ip": "203.0.113.42", "device_platform": "ios"},
"vendor_data": "user-1234",
},
timeout=30,
)
print(response.status_code, response.json())
const response = await fetch("https://verification.didit.me/v3/phone/send/", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
phone_number: "+14155552671",
options: { preferred_channel: "whatsapp", code_size: 6, locale: "en-US" },
vendor_data: "user-1234",
}),
});
const data = await response.json(); // { request_id, status, reason, vendor_data, metadata }
响应(200 OK)
{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Success",
"reason": null,
"vendor_data": "user-1234",
"metadata": null
}
状态值与处理方式
| 状态 | 含义 | 操作 |
|---|---|---|
"Success" | 已为新的验证发送验证码(可能通过短信备用方式发送) | 等待用户提供验证码,然后调用 Check |
"Retry" | 在 5 分钟窗口内第二次发送:为同一待处理验证重新发送验证码,request_id 不变(会计费) | 等待用户提供验证码,然后调用 Check |
"Blocked" | 反欺诈层拒绝发送。reason 会说明原因(例如 suspicious、spam)。不计费。验证会立即结束(状态为 Declined,并附带 HIGH_RISK_PHONE_NUMBER;对于允许名单中的号码,状态为 In Review),因此之后的检查会返回 "Expired or Not Found" | 不要对同一号码重试;请索要另一个号码或进行审核 |
错误响应
| 代码 | 含义 | 操作 |
|---|---|---|
400 | 请求无效 | 查看响应正文中的字段错误。格式可解析但无法接收验证码的号码会返回 {"detail": "Invalid phone number provided."} 或 {"detail": "Invalid phone line type provided."}(例如短代码) |
401 | API 密钥已过期 | 在 Developers > API keys 中轮换密钥或创建新密钥 |
403 | API 密钥缺失、错误或已撤销;受限密钥无权访问此端点;请求来自密钥 IP 允许名单之外;或额度不足 | 查看 detail 或 error 中的消息:修正 x-api-key 标头或密钥访问权限,或充值 |
429 | 速率限制:每个密钥每分钟最多 300 个 POST/PATCH/DELETE 请求和 600 个 GET 请求 | 等待 Retry-After 秒后重试 |
502 | 电话服务提供商暂时不可用:{"detail": "...", "code": "phone_provider_unavailable"} | 稍等片刻后重试。不计费,也不消耗尝试次数 |
电话验证的特殊情况:如果 403 的 detail 表示电话验证已禁用,直到贵组织首次充值(沙盒应用仍可免费测试);以及余额不足以支付目标号码的发送价格时,返回带有 error 的 403。对同一号码尝试次数过多(每小时 4 次)时会返回 429,且不包含 Retry-After 标头:请等待,或使用其他号码。
步骤 2:检查电话验证码
验证验证码并返回电话报告。先调用 Send,并使用相同的 phone_number。
POST https://verification.didit.me/v3/phone/check/
标头:与步骤 1 相同。
正文(JSON)
| 参数 | 类型 | 必填 | 默认值 | 值 | 描述 |
|---|---|---|---|---|---|
phone_number | string | 是 | - | E.164 | 步骤 1 中使用的同一号码(用于将检查与发送关联起来) |
code | string | 是 | - | 4-8 位数字 | 用户收到的验证码 |
duplicated_phone_number_action | string | 否 | "NO_ACTION" | "NO_ACTION" / "DECLINE" | 本应用中已被其他用户使用的号码(vendor_data 不同) |
disposable_number_action | string | 否 | "NO_ACTION" | "NO_ACTION" / "DECLINE" | 临时号码或一次性号码(phone.is_disposable) |
voip_number_action | string | 否 | "NO_ACTION" | "NO_ACTION" / "DECLINE" | 虚拟线路:voip、isp 或 vpn(phone.is_virtual) |
enable_social | boolean | 否 | false | - | 同时查询该号码的社交足迹(添加 phone.phone_social;按请求作为附加项计费) |
phone_no_social_presence_action | string | 否 | "NO_ACTION" | "NO_ACTION" / "DECLINE" | 如果社交足迹检查发现该号码未注册任何平台(profiles_registered: 0),则拒绝。需要 enable_social: true |
策略说明: 如果发现风险,
DECLINE会将正确的验证码判定为"Declined"。NO_ACTION会将风险记录在phone.warnings中(日志类型为information),但不会更改状态。列入黑名单的号码始终会被拒绝。此处仅接受NO_ACTION和DECLINE。
示例
response = requests.post(
"https://verification.didit.me/v3/phone/check/",
headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={
"phone_number": "+14155552671",
"code": "123456",
"disposable_number_action": "DECLINE",
"voip_number_action": "DECLINE",
},
timeout=30,
)
result = response.json()
if result["status"] in ("Approved", "Declined"):
print(result["phone"]["carrier"], result["phone"]["is_virtual"])
const response = await fetch("https://verification.didit.me/v3/phone/check/", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
phone_number: "+14155552671",
code: "123456",
voip_number_action: "DECLINE",
}),
});
const result = await response.json(); // inspect result.status, not the HTTP code
响应(200 OK)
{
"request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
"status": "Approved",
"message": "The verification code is correct.",
"phone": {
"status": "Approved",
"phone_number_prefix": "+1",
"phone_number": "4155552671",
"full_number": "+14155552671",
"country_code": "US",
"country_name": "United States",
"carrier": {"name": "Example Mobile", "type": "mobile"},
"is_disposable": false,
"is_virtual": false,
"verification_method": "whatsapp",
"verification_attempts": 1,
"verified_at": "2026-06-12T01:24:47.311323Z",
"warnings": [],
"lifecycle": [
{"type": "PHONE_VERIFICATION_MESSAGE_SENT", "timestamp": "2026-06-12T01:23:39.580554+00:00", "details": {"status": "Success", "reason": null, "channel": "whatsapp", "actual_channel": "whatsapp"}, "fee": 0.0709},
{"type": "PHONE_DELIVERY_DELIVERED", "timestamp": "2026-06-12T01:23:43.118220+00:00", "details": {"channel": "whatsapp", "status": "delivered"}, "fee": 0},
{"type": "VALID_CODE_ENTERED", "timestamp": "2026-06-12T01:24:47.311201+00:00", "details": {"code_tried": "123456", "status": "Approved"}, "fee": 0},
{"type": "PHONE_VERIFICATION_APPROVED", "timestamp": "2026-06-12T01:24:47.384292+00:00", "details": null, "fee": 0}
],
"matches": []
},
"vendor_data": "user-1234",
"metadata": null,
"created_at": "2026-06-12T01:24:47.401719+00:00"
}
如果验证码错误但仍有剩余尝试次数,则会返回 "status": "Failed"、"message": "The verification code is incorrect. Attempts remaining: 2" 和 "phone": null。
状态值与处理方式
| 状态 | 含义 | request_id / phone | 操作 |
|---|---|---|---|
"Approved" | 代码正确,且未匹配到拒绝风险 | 发送请求的会话 ID / 完整报告 | 手机号已验证,继续 |
"Declined" | 终止状态:代码正确,但匹配到拒绝风险(DECLINE 操作、列入黑名单或高风险号码),或第 3 次输入错误代码 | 发送请求的会话 ID / 完整报告 | 读取 phone.warnings;拦截该号码或要求用户提供另一个号码 |
"Failed" | 代码错误,仍有剩余尝试次数(message 会说明剩余次数) | 一次性 UUID / null | 要求用户重新输入代码 |
"Expired or Not Found" | 最近 5 分钟内没有该号码的待处理验证 | 一次性 UUID / 缺失 | 发送新代码(步骤 1) |
错误响应
| 代码 | 含义 | 操作 |
|---|---|---|
400 | 请求无效 | 读取响应正文中的字段错误(代码不是 4-8 位数字、号码无效、操作不是 NO_ACTION / DECLINE)。不会消耗尝试次数 |
401 | API 密钥已过期 | 轮换密钥,或在 Developers > API keys 中创建新密钥 |
403 | API 密钥缺失、错误或已撤销;受限密钥无权访问此端点;请求来自密钥 IP 允许列表之外;或点数不足 | 读取 detail 或 error 中的消息:修复 x-api-key 标头或密钥权限,或充值 |
429 | 速率限制:每个密钥每分钟最多 300 个 POST/PATCH/DELETE 请求和 600 个 GET 请求 | 等待 Retry-After 秒后重试 |
502 | 电话服务提供商暂时不可用("code": "phone_provider_unavailable") | 短暂退避后重试。不会消耗尝试次数 |
检查免费;唯一的点数检查针对 enable_social 附加功能(余额不足以支付时返回带有 error 的 403)。
手机号风险(不发送代码)
通过一次同步调用对号码进行评分:运营商、线路类型、携号转网、一次性号码、重复项和标准化风险情报。不会发送代码,用户无需执行任何操作。会创建一个 API 会话(request_id)。
POST https://verification.didit.me/v3/phone/risk/
正文(JSON)
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
phone_number | 字符串 | 是 | - | 要评分的号码(最多 20 位,会标准化为 E.164 格式) |
country_code | 字符串 | 否 | - | ISO 3166-1 alpha-2 或 alpha-3 国家/地区提示(最多 3 个字符) |
signals | 对象 | 否 | - | 与步骤 1 中的 signals 字段相同 |
enable_social | 布尔值 | 否 | false | 添加 phone.phone_social 块(按请求收取附加费用) |
vendor_data | 字符串 | 否 | - | 你的参考信息,会被存储并回传 |
metadata | 对象 | 否 | null | 你的 JSON 元数据,会被存储并回传 |
示例
response = requests.post(
"https://verification.didit.me/v3/phone/risk/",
headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={"phone_number": "+14155552671", "country_code": "US", "vendor_data": "lead-123"},
timeout=30,
)
result = response.json()
print(result["status"], result["phone"]["is_virtual"], result["phone"].get("phone_intelligence"))
const response = await fetch("https://verification.didit.me/v3/phone/risk/", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ phone_number: "+14155552671", country_code: "US", vendor_data: "lead-123" }),
});
const result = await response.json();
响应(200 OK)
{
"request_id": "4fd09d2e-9bc9-4c4e-9c46-7f625ac0ab62",
"status": "Approved",
"phone": {
"status": "Approved",
"phone_number_prefix": "+1",
"phone_number": "4155552671",
"full_number": "+14155552671",
"country_code": "US",
"country_name": "United States",
"carrier": {"name": "Example Mobile", "type": "mobile"},
"is_disposable": false,
"is_virtual": false,
"verification_method": null,
"verification_attempts": 0,
"verified_at": null,
"warnings": [],
"lifecycle": [],
"matches": [],
"phone_intelligence": {
"score": 3.0,
"phone_type_risk": "Low",
"phone_trust_index": 97,
"network_stability_index": 95,
"is_disposable": false,
"is_ported": false,
"is_recently_ported": false,
"carrier_type": "mobile",
"reason_codes": []
},
"enrichment": {
"score": 3.0,
"phone_type_risk": "Low",
"phone_trust_index": 97,
"network_stability_index": 95,
"is_disposable": false,
"is_ported": false,
"is_recently_ported": false,
"carrier_type": "mobile",
"reason_codes": []
}
},
"vendor_data": "lead-123",
"metadata": null,
"created_at": "2026-09-01T10:30:00.123456+00:00"
}
status(顶层和phone.status):"Approved"、"In Review"或"Declined"。通常为"Approved";你的应用的手机号风险规则可能会产生其他状态。每次查询完成后,HTTP 状态码均为200。phone.phone_intelligence和phone.enrichment是同一对象(兼容性别名)。这两个键在未返回可用情报时都会缺失。优先依据is_disposable和is_virtual制定规则;缺少相关数据块时应视为“未知”,而非“安全”。如果未能解析出运营商,phone.carrier可能为null。- 查询开始时即会计费,即使缺少尽力获取的字段也不例外。
错误:400 字段错误、401、403(包括此次调用及 enable_social 附加功能的点数检查)、429,与步骤 2 相同。
响应字段参考
phone 对象(检查和风险)
| 字段 | 类型 | 描述 |
|---|---|---|
status | 字符串 | 检查:最终检查结果为 "Approved" 或 "Declined"(枚举还包括 "In Review"、"Not Finished"、"Expired")。风险:"Approved"、"In Review"、"Declined" |
phone_number_prefix | 字符串 | 带 + 的国家/地区拨号代码(例如 +1) |
phone_number | 字符串 | 不含拨号代码的本地号码 |
full_number | 字符串 | 完整的 E.164 号码 |
country_code / country_name | 字符串 | 该号码所属国家/地区的 ISO 3166-1 alpha-2 代码及其名称 |
carrier.name | 字符串 | 运营商名称(未知时为空字符串) |
carrier.type | 字符串 | mobile、fixed_line、voip、isp、vpn、toll_free、premium_rate、pager、payphone、satellite、service、shared_cost、short_codes_commercial、calling_cards、local_rate、universal_access、voice_mail、other、unknown |
is_disposable | 布尔值 | 临时号码或一次性号码服务 |
is_virtual | 布尔值 | 线路类型为 voip、isp 或 vpn |
verification_method | 字符串 | 确认送达后,实际发送验证码的渠道;否则为请求的渠道。风险查询时为 null |
verification_attempts | 整数 | 此次验证发送验证码的次数(1 次;重试后为 2 次)。风险查询时为 0 |
verified_at | 字符串或 null | 输入正确验证码的时间 |
warnings | 数组 | {feature, risk, additional_data, log_type, short_description, long_description}。log_type:error 表示拒绝,warning 表示标记以供审核,information 仅记录 |
lifecycle | 数组 | {type, timestamp, details, fee}。类型:PHONE_VERIFICATION_MESSAGE_SENT、PHONE_VERIFICATION_RETRY_MESSAGE_SENT、PHONE_VERIFICATION_BLOCKED、PHONE_DELIVERY_DELIVERED、PHONE_DELIVERY_UNDELIVERABLE、VALID_CODE_ENTERED、INVALID_CODE_ENTERED、PHONE_VERIFICATION_APPROVED、PHONE_VERIFICATION_DECLINED、PHONE_VERIFICATION_IN_REVIEW、PHONE_VERIFICATION_EXPIRED。发送事件包含 {status, reason, channel, actual_channel};fee(美元)仅在发送事件中非零 |
matches | 数组 | 最多 5 个其他应用会话,其中不同用户(vendor_data)使用了此号码:{session_id, session_number, vendor_data, verification_date, phone_number, status, is_blocklisted, api_service, source}。source 为 session,或在黑名单条目中为 list_entry |
phone_intelligence / enrichment | 对象 | 仅风险端点提供,尽力返回:score(0-100,数值越高风险越高)、phone_type_risk(Low/Medium/High)、phone_trust_index(与评分成反比)、network_stability_index、is_disposable、is_ported、is_recently_ported、carrier_type、reason_codes(开放列表;不要拒绝未知代码) |
phone_social | 对象 | 仅在设置 enable_social: true 且查询完成后提供:profiles_registered / profiles_checked(已注册数量为 0 是真实且可计费的查询结果)、各类数量、0-10 指数、whatsapp_profile_completeness(Basic、Partial、Complete、Not Present)、registered_platforms、not_registered_platforms、categories、profiles。数量可能为 null |
警告标签
| 标签 | 严重性 | 描述 |
|---|---|---|
VERIFICATION_CODE_ATTEMPTS_EXCEEDED | error(始终拒绝) | 独立验证中第 3 次输入错误的验证码 |
PHONE_NUMBER_IN_BLOCKLIST | error(始终拒绝) | 号码位于你的电话黑名单(Lists API)中,或与列入黑名单的会话匹配。additional_data:blocklisted_session_id、blocklisted_session_number、api_service |
HIGH_RISK_PHONE_NUMBER | 默认为 error | 发送时由反欺诈层拦截("Blocked")后触发;请求会被拒绝。位于你的电话允许名单中的号码则会进入 In Review(log_type: "warning",additional_data.phone_number_in_allowlist: true)。additional_data.blocked_reason 保存发送的 reason |
DISPOSABLE_NUMBER_DETECTED | 由 disposable_number_action 决定 | 临时号码或一次性号码 |
VOIP_NUMBER_DETECTED | 由 voip_number_action 决定 | 线路类型为 voip、isp 或 vpn |
DUPLICATED_PHONE_NUMBER | 由 duplicated_phone_number_action 决定 | 其他用户在另一个会话中使用了该号码(任意状态)。additional_data:duplicated_session_id、duplicated_session_number、api_service |
PHONE_NUMBER_IN_ALLOWLIST | information | 该号码与其他会话匹配,但位于你的电话允许名单中,因此跳过了重复号码操作。additional_data:phone_number |
在此 API 中,DECLINE 操作会生成 error 类型的日志,NO_ACTION 会生成 information 类型的日志。
常见工作流
基本电话验证
1. POST /v3/phone/send/ -> {"phone_number": "+14155552671"}
If "Blocked" -> ask for another number (nothing billed)
2. Wait for the user to type the code (valid 5 minutes from the first send)
3. POST /v3/phone/check/ -> {"phone_number": "+14155552671", "code": "123456"}
4. If "Approved" -> phone verified
If "Failed" -> ask the user to retry (3 attempts in total)
If "Declined" -> read phone.warnings
If "Expired or Not Found" -> send again (step 1)
严格安全验证
1. POST /v3/phone/send/ -> include signals.ip, signals.device_platform, vendor_data,
options.preferred_channel: "sms"
2. POST /v3/phone/check/ -> disposable_number_action, voip_number_action,
duplicated_phone_number_action = "DECLINE"
3. If "Declined" -> phone.warnings[].risk says why; block or ask for another number
无需验证码的预筛查
1. POST /v3/phone/risk/ -> {"phone_number": "+14155552671", "vendor_data": "lead-123"}
2. If phone.is_virtual or phone.is_disposable -> reject before sending any OTP
3. Otherwise continue with the send/check flow above
实用脚本
verify_phone.py:从命令行发送验证码、验证验证码并评估电话风险。
# Requires: pip install requests
export DIDIT_API_KEY="your_api_key"
python scripts/verify_phone.py send +14155552671 --channel sms --sms-sender-name "Example Bank"
python scripts/verify_phone.py check +14155552671 123456 --decline-voip --decline-disposable
python scripts/verify_phone.py risk +14155552671 --country-code US
也可以作为库导入:
from scripts.verify_phone import send_code, check_code, score_phone_risk
send_result = send_code("+14155552671", channel="sms")
check_result = check_code("+14155552671", "123456", decline_voip=True)
risk_result = score_phone_risk("+14155552671")