Plonki

Webhooks

Plonki can POST to your server when an order finishes, so you don't have to poll.

Set up

In the dashboard, open Settings, add an HTTPS URL and save. The signing secret (whsec_…) appears under it.

Events

order.completedA registration or renewal finished. The domain is in the account.
order.failedThe registry refused it. Any payment went back to the balance.

Payload

json
{
  "id": "evt_3k8m2q9w4x1n7b5v6c0z",
  "type": "order.completed",
  "created": 1794051896,
  "data": {
    "id": "ord_k2v9wq4x8n1m7b3c5d6f",
    "type": "register",
    "domain": "acmecoffee.com.au",
    "years": 1,
    "amount": 2399,
    "currency": "AUD",
    "status": "completed",
    "payment": "balance",
    "external_ref": "cust_8271",
    "test_mode": false,
    "next_step": "Done. Use point_domain or set_nameservers to connect it to a website.",
    "created_at": "2026-11-08T01:23:45.000Z"
  }
}

Verify the signature

Each request has Plonki-Signature: t=<unix seconds>,v1=<hex>. v1 is HMAC-SHA256 of`${t}.${raw body}` with your signing secret. Compare in constant time and reject anything older than five minutes.

import crypto from "node:crypto";

export function verifyPlonki(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return ok && fresh;
}
import hashlib, hmac, time

def verify_plonki(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    signed = f"{parts['t']}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return hmac.compare_digest(expected, parts.get("v1", "")) and fresh
function verify_plonki(string $rawBody, string $header, string $secret): bool {
    parse_str(str_replace(',', '&', $header), $p);
    $expected = hash_hmac('sha256', $p['t'] . '.' . $rawBody, $secret);
    return hash_equals($expected, $p['v1'] ?? '') && abs(time() - (int) $p['t']) < 300;
}

Delivery