WapFlow OTP API 参考
用您自己的 WhatsApp 号码发送并验证验证码。所有响应均为 JSON。
基础地址: https://wapflow.net/api/otp/v1
身份验证
每个请求都需发送 Authorization: Bearer <key> 请求头。您可在 WapFlow 面板的 WapFlow OTP 页面创建密钥;正式密钥以 wfo_live_ 开头,测试密钥以 wfo_test_ 开头。密钥仅在创建时显示一次——请妥善保存,并且只在服务器端使用。
成功响应形如 { "success": true, ... };错误响应形如 { "success": false, "error": { "code": "...", "message": "..." } }。
发送验证码
phone(必填)为接收者号码。可选项:code_length(4–8)、ttl(60–900 秒)、lang(如 tr、en)和 app_name。未提供的参数将使用您的面板设置。
{
"phone": "905321234567",
"code_length": 6,
"ttl": 300,
"lang": "tr",
"app_name": "Acme"
}
{
"success": true,
"request_id": "req_...",
"phone": "905321234567",
"expires_at": "2026-11-12T10:05:00.000Z",
"status": "sent" // "sent" | "queued"
}
验证验证码
phone 和 code 为必填项。提供 request_id 时,将校验该特定请求;不提供时,则校验使用此密钥发送到该号码的最新未过期验证码。连续 5 次错误尝试后,该请求将被锁定;已验证的验证码不可重复使用。
{ "request_id": "req_...", "phone": "905321234567", "code": "123456" }
{ "success": true, "valid": true }
{ "success": true, "valid": false,
"reason": "invalid_code", // invalid_code | expired | too_many_attempts | already_verified | not_found
"attempts_left": 3 }
查询状态
返回请求状态:queued、sent、delivered、read、failed、verified 或 expired。
{ "success": true, "request_id": "req_...", "status": "delivered",
"sent_at": "...", "delivered_at": "...", "verified_at": null }
测试连接
显示密钥是否有效、您的套餐、已连接账户数量以及本月配额使用情况(WordPress 插件的“测试连接”按钮即使用此接口)。
{ "success": true, "plan": "otp-2",
"accounts": { "limit": 2, "connected": 1 },
"quota": { "monthly": 15000, "used": 42, "period": "2026-11" },
"ends_at": "2026-12-12T10:00:00.000Z", "mode": "live" }
示例
以下示例发送验证码,然后验证用户输入的验证码。请将 wfo_live_XXXX 替换为您自己的密钥。
# 1) send
curl -X POST https://wapflow.net/api/otp/v1/send \
-H "Authorization: Bearer wfo_live_XXXX" \
-H "Content-Type: application/json" \
-d '{"phone":"905321234567","app_name":"Acme"}'
# 2) verify
curl -X POST https://wapflow.net/api/otp/v1/verify \
-H "Authorization: Bearer wfo_live_XXXX" \
-H "Content-Type: application/json" \
-d '{"request_id":"req_...","phone":"905321234567","code":"123456"}'<?php
function wapflow_otp(string $path, array $body): array {
$ch = curl_init('https://wapflow.net/api/otp/v1' . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('WAPFLOW_OTP_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($body),
]);
$res = json_decode(curl_exec($ch), true) ?: [];
curl_close($ch);
return $res;
}
$sent = wapflow_otp('/send', ['phone' => '905321234567', 'app_name' => 'Acme']);
// ... kullanıcı kodu girince:
$check = wapflow_otp('/verify', ['request_id' => $sent['request_id'], 'phone' => '905321234567', 'code' => $_POST['code']]);
if (!empty($check['valid'])) { /* giriş başarılı */ }const BASE = 'https://wapflow.net/api/otp/v1';
async function otp(path, body) {
const res = await fetch(BASE + path, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.WAPFLOW_OTP_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
return res.json();
}
const sent = await otp('/send', { phone: '905321234567', app_name: 'Acme' });
// ... when the user submits the code:
const check = await otp('/verify', { request_id: sent.request_id, phone: '905321234567', code: '123456' });
if (check.valid) { /* signed in */ }import os
import requests
BASE = "https://wapflow.net/api/otp/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["WAPFLOW_OTP_KEY"]}
def otp(path, body):
return requests.post(BASE + path, json=body, headers=HEADERS, timeout=15).json()
sent = otp("/send", {"phone": "905321234567", "app_name": "Acme"})
# ... when the user submits the code:
check = otp("/verify", {"request_id": sent["request_id"], "phone": "905321234567", "code": "123456"})
if check.get("valid"):
pass # signed in错误代码
| HTTP | 代码 | 含义 |
|---|---|---|
| 400 | invalid_request | 请求正文或参数无效。 |
| 401 | invalid_key | API 密钥无效或已被撤销。 |
| 402 | no_subscription | 该账户没有 WapFlow OTP 订阅。 |
| 402 | subscription_expired | 订阅已过期;请续订。 |
| 403 | sales_disabled | 服务暂时不可用。 |
| 422 | invalid_phone | 电话号码无效。 |
| 429 | rate_limited | 超出速率限制;请在响应中 retry_after 秒后重试。 |
| 429 | quota_exceeded | 本月验证码配额已用完。 |
| 429 | daily_limit_reached | 已连接的 WhatsApp 账号的每日发送上限已达到;请在 retry_after 秒后(次日 00:00,伊斯坦布尔时间)重试。不会消耗配额。 |
| 503 | no_account_connected | 没有已连接且已启用的 WhatsApp 账户。 |
| 503 | send_failed | 消息发送失败;请稍后重试。 |
速率限制
- 每个号码:每 60 秒 1 条,每小时 5 条,每天 10 条。
- 每个 API 密钥:每分钟 60 次发送。
- 每个 IP 地址:每分钟 120 次请求(包括验证)。
- 验证:每个号码每分钟 10 次尝试;每个验证码最多 5 次错误尝试。
- 月度配额取决于您的套餐;配额用完后将返回
quota_exceeded。测试密钥不消耗配额。
账号预热与每日上限
- 新连接的 WhatsApp 号码会先进行预热,以降低被封禁的风险:前 3 天每天 30 条验证码,第 4–7 天每天 100 条,之后每个账号每天 500 条(默认值)。
- 达到每日上限的账号会被跳过,验证码改由您已连接的其他账号发送。若全部账号都已达上限,将返回
429 daily_limit_reached;计数器于伊斯坦布尔时间午夜重置。 - 如需更大的发送量,请选择包含多个账号的套餐。测试密钥不受这些限制影响。
测试密钥
以 wfo_test_ 开头的密钥不发送 WhatsApp 消息,也不消耗配额;验证码通过 test_code 字段返回。发送和速率限制规则与正式密钥相同,且仍需有效订阅。正式上线时,只需替换密钥。
{ "success": true, "request_id": "req_...", "status": "sent", "test_code": "482913" }
电话号码格式
- 号码将被简化为纯数字(去除 +、空格和连字符),长度必须为 10–15 位。
- 请包含国家代码(如
905321234567)。以 0 开头的 11 位号码将被视为土耳其号码,并在前面加上 90。 - 无效号码将返回
422 invalid_phone。
安全说明
- 验证码使用加密安全的随机数生成器生成,且绝不以明文形式存储。
- 切勿在浏览器、移动应用或公开代码中使用 API 密钥;请从您的服务器调用 API。
- 请始终通过
/verify在服务器端检查验证码。 - WapFlow OTP 不是官方 WhatsApp API,也不保证送达;对于关键操作,请保留备用渠道。
WordPress 插件
- 下载 wapflow-otp.zip。
- 在 WordPress 后台进入 插件 → 安装插件 → 上传插件,上传 zip 文件并启用。
- 在插件设置中,粘贴您在 WapFlow 面板中创建的 API 密钥。
- 使用“测试连接”按钮检查密钥和您的套餐。
- 先用测试密钥试用,然后再切换为正式密钥。