# Domain Verification Hooks

https://dev.wisecp.com/es/domain-verification-hooks

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 PHP

```php
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 PHP

```php
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 PHP

```php
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 PHP

```php
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 PHP

```php
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 PHP

```php
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 PHP

```php
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 PHP

```php
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.

## Related Articles

- [Domain Contact Hooks](https://dev.wisecp.com/en/domain-contact-hooks)
- [Domain DNS Hooks](https://dev.wisecp.com/en/domain-dns-hooks)
- Domain Hooks
