# Contact verification

> Why an unverified contact e-mail suspends a domain after 15 days, how to check and resend verification, and how to change details mid-verification.

This is the part of the API that most often surprises new integrators. Read it before you register your first domain.

For most domain extensions, registry rules require the registrant's contact data to be verified. There are two independent processes:

| Type | What it verifies | Who gets the e-mail | Started by |
| --- | --- | --- | --- |
| `Email` | That the contact's e-mail address is real and reachable. | The contact's own address. | Creating a domain or contact; changing an e-mail address. |
| `Owner` | That the current owner consents to an ownership change. | The **previous** owner's address. | Changing `firstName`, `lastName` or `companyName` on a contact behind an in-scope domain. |

## Timings you need to design around

| Setting | Value |
| --- | --- |
| Verification link validity | **15 days** from issue |
| Automatic reminder e-mails | every **3 days** while pending |
| Minimum gap between manual re-sends | **1 minute** |
| Maximum re-sends | **100** |

## What happens if nobody verifies

1. You register a domain. It becomes active straight away — **verification is not a precondition for registration**.
2. We open a verification process for each distinct contact e-mail on the domain. One address used for all four roles produces **one** verification, not four.
3. The contact receives a link valid for 15 days, plus a reminder every 3 days.
4. If nobody clicks within 15 days, the domain is **suspended**: its status becomes `PendingHold`, the reason becomes `RddsVerificationPending`, and its name servers are switched to our suspension servers — **the website and e-mail stop working**.
5. When the contact finally verifies, the domain is reactivated automatically. This is not instant — it is handled by a background process shortly afterwards.

:::danger[The practical consequence]
You can register a domain with an unverified contact and it will work. **Fifteen days later it will go dark** unless the registrant clicked the link. If you register domains on behalf of customers, build the verification state into your dashboard and chase it — do not discover it when the customer calls to say their site is down.
:::

## Which extensions this applies to

Verification applies to most generic extensions (`.com`, `.net`, `.org` and the newer gTLDs). A number of country-code extensions are outside its scope and never enter the process.

The practical test: call `GET /api/v1/contacts/{handleCode}`. If `verificationObjects` comes back empty and the contact is not already verified, that handle's domains are outside scope.

## Renewing a suspended domain

A domain suspended for unverified contact data **can still be renewed** — do not let it expire while you chase the verification. Renewing does not lift the suspension; only verification does.

## Changing an e-mail address mid-verification

Changing the address does not reset the clock to zero and does not avoid verification. The new address joins the same verification chain. If the new address is **already verified** from another contact, the chain inherits that verification and suspended domains become eligible for reactivation. The number of address changes within one chain is capped; past the limit only support can intervene.

## GET /api/v1/contacts/verification/check

Current verification state for a handle. This is the endpoint to poll from your dashboard.

**Request**

```http
GET /api/v1/contacts/verification/check?handleCode=D-174829301847
```

**Response — 200**

```json
{
  "results": [
    {
      "fieldType": "Email",
      "fieldValue": "ahmet@apiname.com",
      "ownerName": "Ahmet Yilmaz",
      "operationStatus": "VerificationPending",
      "isUserVerified": false,
      "userVerificationDate": null,
      "tokenExpireDate": "2026-11-14T10:22:31",
      "resendDate": "2026-10-30T10:23:31",
      "resendRetryCount": 100,
      "attemptNumber": 1,
      "lastAttemptDate": "2026-10-30T10:22:31",
      "createdDate": "2026-10-30T10:22:31"
    }
  ]
}
```

**Status values**

| Value | Name | Meaning |
| --- | --- | --- |
| 0 | `None` | Just created. |
| 1 | `VerificationPending` | Waiting for the user. Re-send is allowed. |
| 2 | `Completed` | Finished. |
| 3 | `TokenExpired` | The 15 days ran out. Re-sending issues a fresh link and a fresh 15-day window. |
| 4 | `Cancelled` | Abandoned. |
| 5 | `ResendLimitReached` | Allowance exhausted. Open a support ticket. |
| 6 | `MailChanged` | Superseded because the address changed; the chain continues on the new address. |
| 7 | `OperationPending` | The user responded; we are applying the result. |

:::note[This endpoint errors when there is nothing to report]
Unlike `GET /contacts/{handleCode}`, which returns an empty array, this endpoint returns `Dna.DomainService:Contact:Verification:10005` when no verification exists. That is not a failure — it usually means the handle's extensions are outside verification scope, or verification completed some time ago. Treat it as "nothing pending".
:::

**Errors**

| Code | Meaning and fix |
| --- | --- |
| `Dna.DomainService:Contact:10019` | `handleCode` is empty. |
| `Dna.DomainService:Contact:Verification:10006` | Handle not found in your account. |
| `Dna.DomainService:Contact:Verification:10005` | No verification record. See the note above. |

## POST /api/v1/contacts/verification/request

Re-sends the verification e-mail. Use it when the customer says they never received it, or when the link has expired.

**Request**

```json
{
  "handleCode": "D-174829301847"
}
```

**Response — 204, no body**

:::tip[What this does]
If both an e-mail and an ownership verification are open, **both** notices go out in one call. If the link had already expired, a **new link with a fresh 15-day window** is issued — this is how you revive a lapsed verification without recreating anything.

Recipients differ: the e-mail verification goes to the contact's address; the ownership verification goes to the **previous** owner's address.
:::

**Errors**

| Code | Meaning and fix |
| --- | --- |
| `Dna.DomainService:Contact:Verification:10002` | You are inside the 1-minute cooldown. The message tells you how many seconds to wait. |
| `Dna.DomainService:Contact:Verification:10003` | Already verified — nothing to send. Treat as success. |
| `Dna.DomainService:Contact:Verification:10001` | Send limit reached. Open a support ticket; the customer most likely has a mail-delivery problem. |
| `Dna.DomainService:Contact:Verification:10009` | This verification is already finalised (completed, cancelled or superseded) and cannot be re-sent. |
| `Dna.DomainService:Contact:Verification:10005` | No verification record to re-send against. |
| `Dna.DomainService:Contact:Verification:10004` | The e-mail could not be sent. Try again shortly. |
| `Dna.DomainService:Contact:Verification:10006` | Handle not found in your account. |

## Changing an owner's name when the e-mail is unverified

You cannot change `firstName`, `lastName` or `companyName` while the contact's e-mail is unverified — the request fails with `Dna.DomainService:Contact:Verification:10010`. The reason is that the ownership-change notice is sent to that very address, so it has to be known to work first.

The working sequence:

1. `POST /api/v1/contacts/verification/request` — send the e-mail verification.
2. The customer clicks the link.
3. `GET /api/v1/contacts/verification/check` until `isUserVerified` is `true` for `fieldType: "Email"`.
4. `PUT /api/v1/contacts/{handleCode}` with the new name. This now starts an **ownership** verification, sent to the current owner.
