开发者 → Webhooks

Webhooks

当您的账户发生事件时,NameOcean 会向您的 HTTPS 端点发送一条已签名的 JSON 事件,让您的代码能够及时响应,无需轮询。

添加端点

您可以在 控制面板 → Webhooks 中创建,也可以通过 API 创建,或通过 MCP 让您的 AI 创建(create_webhook)。可选择具体事件类型、事件组(如 dns.*)或全部事件。返回结果中包含签名密钥(whsec_…)。

curl
$ curl -X POST https://nameocean.net/api/v1/webhooks \
    -H "Authorization: Bearer $NAMEOCEAN_API_KEY" -H "Content-Type: application/json" \
    -d '{"url": "https://example.com/hooks/nameocean", "events": ["dns.*", "ssl.issued", "domain.expiring"]}'

URL 必须是公网服务器上的 HTTPS 地址。请在 10 秒内返回任意 2xx 响应;耗时操作请在响应之后再处理。

您将收到的内容

POST https://example.com/hooks/nameocean
Content-Type: application/json
X-NameOcean-Event: dns.record.created
X-NameOcean-Delivery: whd_7c1e0f9a2b3d4e5f6a7b8c9d
X-NameOcean-Signature: t=1794300000,v1=5f2b…

{
  "id": "evt_3a9f1c2e4b5d6a7c8e9f0a1b",
  "type": "dns.record.created",
  "created": "2026-11-10T09:20:00+00:00",
  "data": {
    "zone": "example.com",
    "record": {"id": 4211, "type": "A", "hostname": "www", "ip": "203.0.113.10", "ttl": "1h"}
  }
}

投递重试或重新发送时,事件 id 保持不变:可据此跳过已处理过的事件。事件到达顺序可能与发生顺序不一致。

验证签名

t 是请求的 Unix 时间戳,v1 是以您的密钥对 t、一个点号和原始请求体进行 HMAC-SHA256 计算所得的十六进制值。请在解析 JSON 之前基于原始字节进行计算,使用恒定时间比较,并拒绝超过五分钟的时间戳。

X-NameOcean-Signature
t=1794300000,v1=hex(HMAC_SHA256(secret, "1794300000." + raw_body))
Python
import hashlib, hmac, time

def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
    fields = dict(part.split("=", 1) for part in header.split(","))
    ts = int(fields["t"])
    if abs(time.time() - ts) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, fields.get("v1", ""))
Node.js
import crypto from "node:crypto";

export function verify(secret, header, rawBody, tolerance = 300) {
  const fields = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const ts = Number(fields.t);
  if (Math.abs(Date.now() / 1000 - ts) > tolerance) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${ts}.`).update(rawBody).digest("hex");
  const got = Buffer.from(fields.v1 || "", "hex");
  return got.length === 32 && crypto.timingSafeEqual(Buffer.from(expected, "hex"), got);
}
Go
func Verify(secret, header string, body []byte) bool {
	var ts int64
	var v1 string
	for _, part := range strings.Split(header, ",") {
		k, v, _ := strings.Cut(part, "=")
		switch k {
		case "t":
			ts, _ = strconv.ParseInt(v, 10, 64)
		case "v1":
			v1 = v
		}
	}
	if d := time.Now().Unix() - ts; d > 300 || d < -300 {
		return false
	}
	mac := hmac.New(sha256.New, []byte(secret))
	fmt.Fprintf(mac, "%d.", ts)
	mac.Write(body)
	return hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(v1))
}

重试与停用

若投递未获响应、超时,或返回 5xx、408 或 429,将按 1m, 5m, 30m, 2h, 5h, 10h, 10h 的间隔重试:约一天内共尝试 8 次。其他 4xx 响应将立即判定为失败。连续 5 次投递最终失败后,该端点将被停用,并向您发送邮件通知;您可以在控制面板中重新启用,或使用 PATCH active: true。每次投递的状态码及您响应内容的开头部分都会列在控制面板和 GET /api/v1/webhooks/{id}/deliveries 中,您可以在其中重新发送某次投递。

事件

domain.registered您的账户中注册了一个域名。
domain.renewed域名到期日已延后(续费)。
domain.expiring域名将在 30 天、7 天或 1 天后到期。
domain.expired域名已于昨天到期。
domain.nameservers.updated域名的域名服务器已更改。
domain.transfer.started已向注册局提交转入或转出请求。
domain.transfer.completed转移已完成。
domain.transfer.failed转移已被拒绝或取消。
dns.zone.created已创建 DNS 区域。
dns.zone.deleted已删除 DNS 区域。
dns.record.created已添加 DNS 记录。
dns.record.updatedDNS 记录已更改。
dns.record.deleted已删除 DNS 记录。
ssl.issued证书已首次签发。
ssl.renewed证书已续期。
ssl.expiring证书将在 7 天后到期,且尚未续期。
email.mailbox.created您的某个邮件域名中新增了一个邮箱。
email.mailbox.deleted一个邮箱及其邮件已被删除。
email.forwarding.enabled某个域名已开启邮件转发。
email.forwarding.disabled某个域名的邮件转发已关闭,原因是您手动关闭或该域名不再符合条件(具体原因请查看事件详情)。
webhook.test在您点击“发送测试”时发送;不受该端点事件筛选条件的限制。

data 包含域名或区域以及相关对象:记录(更新时附带 previous)、到期日期、域名服务器(附带 previous)。各对象的具体字段与 REST API 相同。

收到的邮件也可以推送到某个 URL:请在 邮件转发 中为每个地址单独设置。这些推送使用相同的请求头和签名,X-NameOcean-Event 为 email.received。

注意事项

  • NameOcean 仅向公网地址发送推送,每次投递时都会重新检查,且从不跟随重定向。
  • 如果密钥泄露,请在控制面板中轮换密钥;事件将立即使用新密钥签名。
  • “发送测试”会立即推送一条 webhook.test 事件,不受端点事件过滤设置的限制。