URL: /guides/webhooks

---
title: Configure webhooks
description: Subscribe an HTTPS endpoint to domain lifecycle and billing events.
---

Webhook setup is in the dashboard. Configure once; subscribe to the events you care about. The full event list is in the [API webhooks reference](/api/webhooks).

## Add an endpoint

[Dashboard → Settings → Webhooks](https://app.domaingenius.com.au/dashboard/settings/webhooks).

<Steps>
  <Step title="Add URL">
    Public HTTPS URL. We won't post to plain HTTP.
  </Step>
  <Step title="Pick events">
    All-or-individual. Start narrow.
  </Step>
  <Step title="Copy the signing secret">
    Shown once. Used to verify each request — see [API webhooks](/api/webhooks#verify-the-signature).
  </Step>
  <Step title="Send a test">
    The dashboard's **Send test event** button posts a synthetic `domain.registered` with `id: evt_test_...`. Useful for verifying your handler before any real event lands.
  </Step>
</Steps>

## What to do in your handler

- Verify the signature first. Reject unsigned requests.
- Read the event id and check for duplicates — we may retry, and a network blip can cause re-delivery after you 200'd. Build idempotency on `event.id`.
- Enqueue the work and return 200 quickly. Long handlers cause timeouts and retries.

## Local development

ngrok or a Cloudflare quick tunnel works. The dashboard accepts any public HTTPS URL.

```bash
ngrok http 8787
# Set webhook URL to the ngrok https URL
```

The dashboard shows recent deliveries with response codes. Bad endpoints get exponential backoff (30s → 24h) and pause after 24h of failures.

## Replay

Need to re-deliver a missed event? In the dashboard, open the event detail and click **Replay**. The endpoint receives the original payload with the same `id` — your idempotency layer should treat it as a no-op or proceed depending on intent.

## Internal webhooks

The endpoints under `/api/v1/webhooks/stripe` and `/api/v1/webhooks/clerk` are inbound — Stripe and Clerk POST to us. They are not user-configurable.
