Webhooks
Set up signed Porkbun webhooks so a transfer that completes overnight shows as active in about a minute instead of waiting for the domain sync cron.
Optional, and worth the ten minutes.
What they fix
Without webhooks, WHMCS only learns about registrar-side changes when the domain sync cron runs. A transfer that completes at 2am leaves the customer looking at Pending Transfer until the cron catches up — potentially most of the next day.
With webhooks, Porkbun pushes the change and WHMCS reflects it in about a minute.
Before you start
Porkbun will only deliver to a URL that is:
https://, on port 443- resolving to a public address
Private, loopback, link-local and CGNAT targets are refused — and this is re-checked immediately before every delivery, not just at registration.
Many WHMCS installations also block direct web access to /modules/. Confirm your URL loads in a browser before registering it; it should answer 405 POST only.
Setup
1. Register the endpoint
cd /path-to-whmcs/modules/registrars/porkbun
php webhook.php --register https://billing.example.com/modules/registrars/porkbun/webhook.phpThis prints a signing secret. Re-running the command is safe: it reuses an endpoint already pointing at that URL, and re-enables it if Porkbun had disabled it.
2. Paste the secret into WHMCS
Put it in Webhook Signing Secret on the registrar configuration page and save. An empty field disables the receiver — deliveries are rejected rather than processed.
3. Confirm the round trip
php webhook.php --testThen look for a Webhook Test entry in Configuration → System Logs → Module Log.
What it does with each event
The endpoint subscribes to *, so new Porkbun event types arrive automatically. Anything without a handler is logged and acknowledged.
| Event | Action |
|---|---|
domain.transfer.completed | Clears Pending Transfer to Active and writes the new expiry |
domain.renewed | Updates the expiry — including renewals WHMCS did not initiate, such as Porkbun auto-renew |
domain.registered | Updates the expiry. If no WHMCS record matches, raises an alert — a domain on the account nobody is billed for |
domain.expiring | Compares against WHMCS and flags a disagreement. Takes no action; WHMCS sends its own renewal notices |
dns.record.* and everything else | Logged only |
Three things worth knowing about the behaviour
It verifies, then re-checks. Every handler that changes WHMCS re-reads the domain from the API first. Only the event envelope is documented as stable, so nothing that touches a customer record rests on a payload field.
It never touches billing. A renewal Porkbun performed may have no matching WHMCS invoice. The expiry date is corrected and an entry written to the activity log; whether to invoice stays a human decision.
Deliveries are deduplicated on the event id — Porkbun may send one twice, and a manual resend deliberately reuses the id. The table mod_porkbun_webhook_events is created automatically and pruned after 30 days. A delivery that fails mid-processing releases its claim so Porkbun’s retry can pick it up.
Security
Every delivery is verified before it is parsed:
- HMAC-SHA256 signature over the exact bytes received, using your signing secret, compared in constant time
- Timestamp check — anything signed more than five minutes ago is rejected, which limits replay
- Event deduplication, so a replayed valid delivery is acknowledged without being processed twice
A delivery that fails any of these is logged and rejected.
Keep the domain sync cron enabled
Webhooks give you speed, not certainty. An endpoint that fails 20 deliveries in a row is disabled at Porkbun’s end, and the cron is what notices and reconciles.
Other commands
php webhook.php --list endpoints, subscriptions and status
php webhook.php --test [id] send a test event
php webhook.php --disable <id> pause deliveries