多H云 Docs
文档中心 / 邮件验证码 OTP

邮件验证码 OTP API

用于登录、注册、找回密码等场景:由你的服务端调用多H云接口,向用户邮箱发送 6 位数字验证码并完成校验。验证码一次性有效,校验成功后立即作废。

Base URLhttps://cloud.cosmicps.com/store-api/api
完整地址示例:https://cloud.cosmicps.com/store-api/api/otp/v1/send

鉴权

在请求头携带 API Key(控制台「API 密钥」页面复制,格式形如 dh_…):

X-Api-Key: dh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

也可放在 JSON Body 的 apiKey 字段(不推荐,易进日志)。未开通邮件推送权益或 Key 已禁用时会返回错误。

说明
获取 Key开通「邮件推送」或「云起步套餐」后自动发放;控制台可轮换 / 新建 / 禁用
多密钥最多 5 个有效 Key;可设置 IP 白名单(控制台保存,用于运维约定)
轮换轮换后旧 Key 立即失效,请同步更新业务配置

发送验证码

POST /otp/v1/send

请求头

Header必填说明
Content-Typeapplication/json
X-Api-Key你的 API Key

请求体

字段类型必填说明
tostring收件人邮箱(用户邮箱)
scenestring业务场景标记,如 login / register / reset,写入邮件模板可用变量并便于你方统计
varsobject自定义模板变量,键值会替换模板中的 {{key}}
apiKeystring同 Header;不推荐

成功响应

{
  "code": 0,
  "data": {
    "requestId": "a1b2c3d4e5f67890",
    "to": "user@example.com",
    "expiresInSec": 600,
    "from": "your-brand@mail.cosmicps.com"
  }
}
字段说明
requestId本次发送记录 ID(内部使用)
expiresInSec有效秒数,默认 600(可由控制台 TTL 配置)
from实际发件地址
响应不会返回验证码明文。验证码仅出现在用户邮箱中。

校验验证码

POST /otp/v1/verify

请求体

字段类型必填说明
tostring与发送时相同的邮箱
codestring用户输入的 6 位验证码

成功 / 失败(HTTP 200,看 data.ok)

// 正确
{ "code": 0, "data": { "ok": true } }

// 错误或过期(仍可能 HTTP 200)
{ "code": 0, "data": { "ok": false, "msg": "验证码错误" } }
{ "code": 0, "data": { "ok": false, "msg": "验证码已过期" } }

校验成功后该验证码立即删除,不可重复使用。请在你的业务里:data.ok === true 时再发放登录态。

错误码与 HTTP 状态

HTTPcodemsg 示例处理建议
401401无效 API Key检查 Key 是否复制完整、是否已轮换/禁用
403403邮箱验证码权益已过期或未开通控制台开通邮件推送或云起步套餐并续费
429429发送过于频繁触发每分钟限流;默认 30 次/分钟,可在控制台调低
400400收件邮箱无效校验 to 格式
4xx/5xx*其他msg;发信通道异常时可能失败,可重试并查控制台用量

统一错误外形:

{ "code": 401, "msg": "无效 API Key" }

控制台配置(影响邮件内容)

路径:控制台 → 邮件推送

配置项说明默认
发件显示名邮件 From 名称与主题前缀多H云
专属发信地址{fromLocal}@mail.cosmicps.com按账号派生
Reply-To用户点回复时的地址你的登录邮箱
正文模板支持 {{code}} {{ttl}} {{scene}} 及 vars 自定义键您的验证码是 {{code}}…
TTL(分钟)1–6010
每分钟上限1–120,按账号维度限流30

代码示例

cURL

# 发送
curl -sS -X POST 'https://cloud.cosmicps.com/store-api/api/otp/v1/send' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: dh_YOUR_KEY' \
  -d '{"to":"user@example.com","scene":"login"}'

# 校验
curl -sS -X POST 'https://cloud.cosmicps.com/store-api/api/otp/v1/verify' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: dh_YOUR_KEY' \
  -d '{"to":"user@example.com","code":"482915"}'

JavaScript(Node / 后端)

const BASE = 'https://cloud.cosmicps.com/store-api/api';
const KEY = process.env.DUOH_OTP_KEY;

async function sendOtp(to, scene = 'login') {
  const r = await fetch(BASE + '/otp/v1/send', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': KEY
    },
    body: JSON.stringify({ to, scene })
  });
  const j = await r.json();
  if (!r.ok || j.code) throw new Error(j.msg || 'send failed');
  return j.data; // { requestId, to, expiresInSec, from }
}

async function verifyOtp(to, code) {
  const r = await fetch(BASE + '/otp/v1/verify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': KEY
    },
    body: JSON.stringify({ to, code })
  });
  const j = await r.json();
  if (!r.ok || j.code) throw new Error(j.msg || 'verify failed');
  return j.data; // { ok: true } | { ok: false, msg }
}

// 登录流程示意
// 1) await sendOtp(email, 'login')
// 2) 用户输入 code 后:
const result = await verifyOtp(email, code);
if (result.ok) {
  // 签发你自己的 session / JWT
} else {
  // 提示 result.msg
}

Python

import os, requests

BASE = "https://cloud.cosmicps.com/store-api/api"
KEY = os.environ["DUOH_OTP_KEY"]
H = {"Content-Type": "application/json", "X-Api-Key": KEY}

def send_otp(to: str, scene: str = "login"):
    r = requests.post(f"{BASE}/otp/v1/send", headers=H, json={"to": to, "scene": scene}, timeout=20)
    j = r.json()
    if r.status_code >= 400 or j.get("code"):
        raise RuntimeError(j.get("msg") or r.text)
    return j["data"]

def verify_otp(to: str, code: str):
    r = requests.post(f"{BASE}/otp/v1/verify", headers=H, json={"to": to, "code": code}, timeout=20)
    j = r.json()
    if r.status_code >= 400 or j.get("code"):
        raise RuntimeError(j.get("msg") or r.text)
    return j["data"]

最佳实践

  1. 只在服务端持有 API Key;浏览器调你自己的 /auth/send-code
  2. 对同一邮箱做业务侧冷却(如 60s),减少 429 与骚扰投诉。
  3. scene 建议固定枚举:login / register / reset / bind
  4. 校验成功后再创建会话;失败不要泄露「邮箱是否注册」以外的细节时可统一文案「验证码无效」。
  5. 生产环境开启密钥轮换与 IP 白名单约定;Key 泄露立即轮换。
  6. 在控制台「邮件推送」查看今日/本月发送与限流次数,异常时先查权益是否过期。
遇到发信失败或审核类问题,请在控制台提交工单;审核类通知会发至运营邮箱处理。