Разработчикам → Webhooks

Webhooks

NameOcean отправляет подписанное JSON-событие на Ваш HTTPS-эндпоинт, когда в Вашем аккаунте что-то происходит, чтобы Ваш код мог реагировать, а не опрашивать API.

Добавить эндпоинт

В разделе Панель управления → Вебхуки, через API или попросив Ваш ИИ через MCP (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 и находиться на публичном сервере. Ответьте любым кодом 2xx в течение 10 секунд; долгую обработку выполняйте после ответа.

Что Вы получаете

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"}
  }
}

Идентификатор события не меняется при повторной или ручной отправке: используйте его, чтобы пропускать уже обработанные события. События могут приходить не по порядку.

Проверка подписи

t — это Unix-время запроса, v1 — шестнадцатеричный HMAC-SHA256 от строки, состоящей из t, точки и исходного тела запроса, вычисленный с Вашим секретным ключом. Вычисляйте его по исходным байтам до разбора 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.updatedNS-серверы домена изменены.
domain.transfer.startedВ реестре запрошен входящий или исходящий трансфер.
domain.transfer.completedТрансфер завершён.
domain.transfer.failedТрансфер отклонён или отменён.
dns.zone.createdСоздана DNS-зона.
dns.zone.deletedУдалена DNS-зона.
dns.record.createdДобавлена DNS-запись.
dns.record.updatedИзменена DNS-запись.
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, независимо от фильтра событий эндпоинта.