URL: /api/webhooks

---
title: Webhooks
description: Event delivery for domain lifecycle and billing changes.
---

DomainGenius posts JSON events to your endpoint over HTTPS when something changes server-side — registration completing, balance dropping, a domain expiring soon.

## Configure

In the dashboard, go to [Settings → Webhooks](https://app.domaingenius.com.au/dashboard/settings/webhooks) and add an endpoint URL. Pick the events you care about. We sign every request.

## Events

| Event | Fires when |
| --- | --- |
| `domain.registered` | Registry confirms registration |
| `domain.transfer.started` | Transfer-in initiated, awaiting auth code |
| `domain.transfer.completed` | Transfer-in finished |
| `domain.expiring.30d` / `.7d` / `.1d` | Domain expiry within window, no auto-renew |
| `domain.expired` | Past expiry |
| `domain.deleted` | Registry-deleted (after grace) |
| `dns.changed` | A record was created, updated, or deleted |
| `balance.low` | Org balance dropped below the auto-top-up trigger |
| `invoice.paid` | Top-up or subscription invoice paid |

## Payload shape

```json
{
  "id": "evt_01HF82...",
  "type": "domain.registered",
  "created": "2026-05-01T08:42:00Z",
  "data": {
    "domain": "example.com.au",
    "org_oid": "org_01HF80...",
    "expires_at": "2027-05-01T00:00:00Z"
  }
}
```

## Verify the signature

Every request carries `DG-Signature: t=<unix>,v1=<hex>`:

```ts
import crypto from "node:crypto";

const verify = (raw: string, header: string, secret: string) => {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${raw}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
};
```

Reject requests where `t` is older than five minutes; that's the replay window.

## Retries

We retry non-2xx responses with exponential backoff: 30s, 2m, 10m, 1h, 6h, 24h. After 24h of failures the endpoint is paused and you get an email.

Respond `200 OK` as soon as you've durably enqueued the event. Long handlers cause timeouts and retries.

## Inbound webhooks

The endpoints under `/api/v1/webhooks/stripe` and `/api/v1/webhooks/clerk` are inbound — Stripe and Clerk POST to us. They are not configurable from your side and not part of the public API surface.
