Webhook guide
Webhook guide

PlanetDomains webhook guide

Webhook is configured from the bot and sends all supported domain events for the user, even when the change did not come from the API itself.

Overview

HTTPS only

Webhook is configured from the bot. Delivery succeeds on any 2xx response.

Events

v1
domain.registered
Triggered after a successful registration.
domain.renewed
Triggered after a successful renewal.
domain.blocked
Triggered when monitoring moves the domain into blocked state.
domain.unblocked
Triggered when the blocked signal is cleared again.

Delivery model

Outbox + retry

Non-2xx responses move the event through retries: 30s, 2m, 10m, 1h, 6h, then failed.

30s
2m
10m
1h
6h

Signature verification

HMAC-SHA256

PlanetDomains signs every payload over timestamp + "." + raw_body.

X-Planet-Event
Event name.
X-Planet-Event-Id
Unique delivery id.
X-Planet-Timestamp
UNIX timestamp string.
X-Planet-Signature
Hex digest of the computed HMAC.

Delivery log API

GET /api/v1/webhook/deliveries

Use this endpoint to inspect the latest webhook attempts for your account: event type, attempt number, status, response code, and the last delivery error.

curl -H "Authorization: Bearer pd_xxxxxxxxxxxxxxxxx" \
  "__BASE_URL__/api/v1/webhook/deliveries?limit=10"
Delivery history response
{
  "status": true,
  "result": {
    "limit": 10,
    "count": 2,
    "items": [
      {"event_key": "evt_renew_001", "event_type": "domain.renewed", "attempt_number": 1, "status": "delivered", "status_code": 200, "error": null},
      {"event_key": "evt_block_002", "event_type": "domain.blocked", "attempt_number": 2, "status": "retrying", "status_code": 500, "error": "upstream timeout"}
    ]
  }
}

Examples

Headers, payload, verify
Webhook payload
{
  "event": "domain.renewed",
  "domain": "planet-project.click",
  "client_ref": "deal-42",
  "external_ref": "invoice-77",
  "occurred_at": "2026-05-15T12:00:00Z"
}
Python
import hmac
import hashlib

signed = f"{timestamp}.{raw_body.decode()}".encode()
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, signature)
Node.js
import crypto from "node:crypto";

const signed = `${timestamp}.${rawBody}`;
const expected = crypto.createHmac("sha256", secret).update(signed).digest("hex");
if (expected !== signature) throw new Error("invalid signature");
PHP
$signed = $timestamp . "." . $rawBody;
$expected = hash_hmac("sha256", $signed, $secret);
if (!hash_equals($expected, $signature)) {
    throw new RuntimeException("invalid signature");
}