Domain Verification Hooks

1 views Markdown

The nine hooks over registrant verification and reading from the provider — even a read has a gate of its own.

Overview

Some extensions ask the registrant to prove who they are with a document. An unverified name can be suspended, so that flow has hooks of its own: a gate stopping the submission, an event reporting the outcome, and a filter handing you the whole screen.

The second group is less known: read gates. Reading name servers or contact details means a real request to the provider, so a gate stands in front of a read as well.

Reference

Stopping a verification submission

gatedomain.verification_submit
ClientDomains before any file is handled

Runs before the submitted documents are handled and nothing is saved yet.

Parameters 3
$servicearrayThe domain service record.
$defsarrayThe field definitions the extension asks for: each with key, type, name, required, options. They differ by extension and module.
$tldstringThe name's extension.
Return 1
stringA non-empty string stops the submission; no file is handled and no field is written.
Listener
Hook::add('gate:domain.verification_submit', 10, function ($service, $defs, $tld) {
    // A second submission while a review is open only causes confusion.
    if (Acme::reviewOpen((int) ($service['id'] ?? 0)))
        return 'Your earlier submission is still under review.';

    return null;
});

Following a verification submission

actiondomain.verification_submitted
ClientDomains how many fields landed

Runs after the documents were handled and the fields saved.

Parameters 3
$servicearrayThe domain service record.
$submittedintHow many fields were saved. It can be zero, meaning a submission happened and no field landed.
$defsarrayThe extension's field definitions.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:domain.verification_submitted', 10,
    function ($service, $submitted, $defs) {
        // Zero fields means the submission was empty: keep it out of the queue.
        if ($submitted > 0) Ops::queue('registrant-review', $service['id'] ?? 0);
    });

Changing the verification screen

filterdomain.verification_state
Hook::runRefs keep all three keys

Runs after the verification screen's whole payload was built, before it appears.

Parameters 2
$dataarrayrefThe screen payload: state (none, form, review, rejected, verified), fields, operator_note. state picks which shell is shown; leave all three keys in place.
$servicearrayThe domain service record.
Return 1
voidThe value changes by reference; the return is not read.
Listener
Hook::add('filter:domain.verification_state', 10, function (&$data, $service) {
    // Leave state, fields and operator_note in place; the shell follows state.
    if (($data['state'] ?? '') === 'rejected')
        $data['operator_note'] = Acme::helpText($data['operator_note'] ?? '');
});

Stopping a contact read

gatedomain.contacts_get
AdminServices a read

Runs before the contact details are read from the provider.

Parameters 2
$servicearrayThe domain service record.
$serviceIdintThe service id.
Return 1
stringA non-empty string stops the read; the text is thrown as the error and the provider is never reached.
Listener
Hook::add('gate:domain.contacts_get', 10, function ($service, $serviceId) {
    // Where the provider charges per query, hold the reads down.
    if (Acme::readQuotaSpent($serviceId)) return 'Today\'s query allowance is spent.';

    return null;
});

Following contact details that were read

actiondomain.contacts_fetched
AdminServices straight from the provider

Runs after the contact details came back from the provider.

Parameters 2
$servicearrayThe domain service record.
$whoisarrayThe contact data read from the provider.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:domain.contacts_fetched', 10, function ($service, $whois) {
    // Personal data: do not log it, only check it against our mirror.
    Acme::compareMirror((int) ($service['id'] ?? 0), $whois);
});

Stopping a name server read

gatedomain.nameservers_get
AdminServices a read

Runs before the name servers are read from the provider.

Parameters 2
$servicearrayThe domain service record.
$serviceIdintThe service id.
Return 1
stringA non-empty string stops the read; the text is thrown as the error and the provider is never reached.
Listener
Hook::add('gate:domain.nameservers_get', 10, function ($service, $serviceId) {
    if (($service['status'] ?? '') === 'transfer')
        return 'Name servers cannot be read while a transfer runs.';

    return null;
});

Following name servers that were read

actiondomain.nameservers_fetched
AdminServices a normalised list

Runs after the name servers came back from the provider.

Parameters 2
$servicearrayThe domain service record.
$nameserversarrayThe list that was read, normalised and ordered.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:domain.nameservers_fetched', 10, function ($service, $nameservers) {
    // A provider value different from ours means the two have drifted.
    Acme::driftCheck((int) ($service['id'] ?? 0), $nameservers);
});

Stopping a DNS record read

gatedomain.dns_records_get
AdminServices a read

Runs before the DNS records are read from the provider.

Parameters 2
$servicearrayThe domain service record.
$serviceIdintThe service id.
Return 1
stringA non-empty string stops the read; the text is thrown as the error and the provider is never reached.
Listener
Hook::add('gate:domain.dns_records_get', 10, function ($service, $serviceId) {
    // Without the DNS management add-on, reading makes no sense either.
    if (!Acme::addonActive($serviceId, 'dns-manage')) return 'DNS management is off.';

    return null;
});

Pitfalls

A read gate leaves the screen empty

Stopping a read does more than cut the request: that part of the screen never fills, and the customer or operator may not see why. The text you return is shown to them, so say what was stopped and why in that sentence.

Contact data that was read is personal data

The contact read hook hands you names, addresses, phone numbers and e-mail. Do not put those in logs, outside systems or notification bodies. Where you need a comparison, keep whether a difference exists rather than the values.

The verification screen draws from three keys

state picks the shell, fields fills it, and operator_note carries the rejection reason. Dropping one breaks the screen: without state, for one, there is no telling which shell to draw.

Zero fields is still a submission

The verification event also runs where no field landed, with the counter at 0. Assuming "a document arrived" without reading the number drops an empty submission into the review queue.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.