Desarrolladores → Webhooks

Webhooks

NameOcean envía un evento JSON firmado a su endpoint HTTPS cuando ocurre algo en su cuenta, para que su código pueda reaccionar en lugar de consultar periódicamente.

Añadir un endpoint

En Panel → Webhooks, con la API, o pidiéndoselo a su IA a través de MCP (create_webhook). Elija tipos de eventos, grupos como dns.*, o todos los eventos. La respuesta incluye el secreto de firma (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"]}'

La URL debe ser https en un servidor público. Responda con cualquier 2xx en un plazo de 10 segundos; realice las tareas lentas después de responder.

Qué recibe

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

El id del evento se mantiene igual cuando una entrega se reintenta o se reenvía: úselo para omitir los eventos que ya haya procesado. Los eventos pueden llegar desordenados.

Verificar la firma

t es la hora Unix de la solicitud y v1 es el HMAC-SHA256 en hexadecimal de t, un punto y el cuerpo sin procesar, firmado con su clave secreta. Calcúlelo sobre los bytes sin procesar, antes de analizar el JSON, compárelo en tiempo constante y rechace las marcas de tiempo con más de cinco minutos de antigüedad.

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

Reintentos y desactivación

Una entrega que no recibe respuesta, agota el tiempo de espera o recibe un 5xx, 408 o 429 se reintenta tras 1m, 5m, 30m, 2h, 5h, 10h, 10h: 8 intentos a lo largo de aproximadamente un día. Cualquier otro 4xx falla de inmediato. Cuando 5 entregas consecutivas fallan definitivamente, el endpoint se desactiva y usted recibe un correo electrónico; vuelva a activarlo en el panel o con PATCH active: true. Cada entrega, con el código de estado y el inicio de su respuesta, aparece en el panel y en GET /api/v1/webhooks/{id}/deliveries, donde puede reenviarla.

Eventos

domain.registeredSe registró un dominio en su cuenta.
domain.renewedLa fecha de vencimiento de un dominio se ha ampliado (renovación).
domain.expiringUn dominio vence en 30, 7 o 1 días.
domain.expiredUn dominio venció ayer.
domain.nameservers.updatedLos servidores de nombres de un dominio han cambiado.
domain.transfer.startedSe ha solicitado una transferencia entrante o saliente en el registro.
domain.transfer.completedUna transferencia ha finalizado.
domain.transfer.failedUna transferencia ha sido rechazada o cancelada.
dns.zone.createdSe ha creado una zona DNS.
dns.zone.deletedSe ha eliminado una zona DNS.
dns.record.createdSe ha añadido un registro DNS.
dns.record.updatedSe ha modificado un registro DNS.
dns.record.deletedSe ha eliminado un registro DNS.
ssl.issuedSe ha emitido un certificado por primera vez.
ssl.renewedSe ha renovado un certificado.
ssl.expiringUn certificado vence en 7 días y aún no se ha renovado.
email.mailbox.createdSe añadió un buzón a uno de sus dominios de correo.
email.mailbox.deletedSe eliminaron un buzón y su correo.
email.forwarding.enabledSe activó el reenvío de correo para un dominio.
email.forwarding.disabledSe desactivó el reenvío de correo para un dominio, por usted o porque el dominio dejó de cumplir los requisitos (el motivo figura en el evento).
webhook.testSe envía cuando pulsa Enviar prueba; ignora el filtro de eventos del endpoint.

data contiene el dominio o la zona y el objeto: el registro (con previous en las actualizaciones), la fecha de caducidad, los servidores de nombres (con previous). Los campos exactos de cada objeto son los de la API REST.

El correo entrante también puede enviarse a una URL: configúrelo para cada dirección en Reenvío de correo. Estos envíos usan los mismos encabezados y la misma firma, con X-NameOcean-Event: email.received.

Conviene saber

  • NameOcean solo envía a direcciones públicas, que se vuelven a comprobar en cada entrega, y nunca sigue redirecciones.
  • Rote el secreto en el panel si se filtra; los eventos se firman con el nuevo de inmediato.
  • Enviar prueba envía un evento webhook.test al instante, independientemente del filtro de eventos del endpoint.