Domain Contacts and Privacy
The nine endpoints governing who a domain is registered to and who sees it.
Overview
Every domain carries contact details in four roles: the registrant, the administrative, the technical and the billing one. They sit at the registry and on most extensions a public lookup shows them.
Contact profiles are kept at the account level to save writing the same details onto every domain. A profile applies to all four roles at once, and the default one is used by itself on new registrations.
The privacy setting stops those details showing in a public lookup. It is free on some extensions and wants an add-on on others.
Reference
Reading the Domain Contacts
Returns the contact details in a domain's four roles.
curl 'https://panel.example.com/api/v1/client/domains/example.com/whois' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/domains/${domain}/whois`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
renderContact(data.registrant);$ch = curl_init('https://panel.example.com/api/v1/client/domains/' . $domain . '/whois');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// All four roles ALWAYS come back; where the provider keeps one contact, all four carry it.
$w = Kernel::internal('client:Domains/GetWhoisContacts',
['owner_id' => $uid, 'domain' => $domain])['data'];
$same = $w['registrant']['email'] === $w['technical']['email'];Writing the Domain Contacts
Writes one contact into a chosen role or into all four.
curl -X PUT 'https://panel.example.com/api/v1/client/domains/example.com/whois' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"role":"technical","contact":{"first_name":"Jane","last_name":"Cooper","email":"[email protected]","phone":"+15551112233","address":"Sample St 42","city":"Austin","state":"TX","zip":"73301","country":"US"}}'const res = await fetch(`https://panel.example.com/api/v1/client/domains/${domain}/whois`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ role: 'technical', contact }),
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/' . $domain . '/whois');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['role' => 'technical', 'contact' => $contact]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Changing THE REGISTRANT counts as a change of holder on some extensions and locks the domain.
// Name the role plainly to change one; the default writes into ALL FOUR.
Kernel::internal('client:Domains/UpdateWhoisContacts', ['owner_id' => $uid, 'domain' => $domain,
'role' => 'technical', 'contact' => $contact]);Applying a Saved Profile
Writes a saved contact profile into all four roles of the domain.
curl -X POST 'https://panel.example.com/api/v1/client/domains/example.com/whois/apply' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"profile_id":7}'const res = await fetch(`https://panel.example.com/api/v1/client/domains/${domain}/whois/apply`, {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ profile_id: profileId }),
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/' . $domain . '/whois/apply');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['profile_id' => $pid]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// This writes over ALL FOUR roles: use the write endpoint with a role to change the technical one alone.
Kernel::internal('client:Domains/ApplyWhoisProfile',
['owner_id' => $uid, 'domain' => $domain, 'profile_id' => $pid]);Changing the Registration Privacy
Opens or closes the hiding of contact details in a public lookup.
curl -X PUT 'https://panel.example.com/api/v1/client/domains/example.com/whois-privacy' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"enabled":true}'const res = await fetch(`https://panel.example.com/api/v1/client/domains/${domain}/whois-privacy`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ enabled: true }),
});
if (res.status === 422) offerAddon(await res.json());$ch = curl_init('https://panel.example.com/api/v1/client/domains/' . $domain . '/whois-privacy');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['enabled' => true]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Privacy is FREE on some extensions and an ADD-ON on others: read the refusal and offer the purchase.
$d = Kernel::internal('client:Domains/GetDomain',
['owner_id' => $uid, 'domain' => $domain])['data'];
$state = $d['addons']['whois_privacy']['state'];Listing the Saved Profiles
Returns the account's saved contact profiles.
curl 'https://panel.example.com/api/v1/client/domains/whois-profiles' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/domains/whois-profiles', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
const fallback = data.find((p) => p.is_default);$ch = curl_init('https://panel.example.com/api/v1/client/domains/whois-profiles');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The default profile applies by itself on NEW registrations and never touches the domains you hold.
$rows = Kernel::internal('client:Domains/GetWhoisProfiles', ['owner_id' => $uid])['data'];
$def = array_values(array_filter($rows, fn ($p) => $p['is_default']))[0] ?? null;Creating a Profile
Saves a contact profile you can reuse.
curl -X POST 'https://panel.example.com/api/v1/client/domains/whois-profiles' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Company","contact":{"first_name":"Jane","last_name":"Cooper","email":"[email protected]","phone":"+15551112233","address":"Sample St 42","city":"Austin","zip":"73301","country":"US"}}'const res = await fetch('https://panel.example.com/api/v1/client/domains/whois-profiles', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, contact }),
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/whois-profiles');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(compact('name', 'contact')),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The STATE is not required on a profile and CAN BE on a domain; filling it in is the safe road.
Kernel::internal('client:Domains/CreateWhoisProfile',
['owner_id' => $uid, 'name' => $name, 'contact' => $contact]);Updating a Profile
Rewrites a saved profile.
curl -X PUT 'https://panel.example.com/api/v1/client/domains/whois-profiles/7' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Company","contact":{"first_name":"Jane","last_name":"Cooper","email":"[email protected]","phone":"+15551112233","address":"New St 7","city":"Austin","zip":"73301","country":"US"}}'const res = await fetch(`https://panel.example.com/api/v1/client/domains/whois-profiles/${pid}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, contact }),
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/whois-profiles/' . $pid);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(compact('name', 'contact')),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Updating a profile DOES NOT reach the domains USING it: apply it again to each of them.
Kernel::internal('client:Domains/UpdateWhoisProfile',
['owner_id' => $uid, 'pid' => $pid, 'name' => $name, 'contact' => $contact]);
foreach ($domains as $d)
Kernel::internal('client:Domains/ApplyWhoisProfile',
['owner_id' => $uid, 'domain' => $d, 'profile_id' => $pid]);Removing a Profile
Removes a saved contact profile.
curl -X DELETE 'https://panel.example.com/api/v1/client/domains/whois-profiles/7' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/domains/whois-profiles/${pid}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${clientKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/whois-profiles/' . $pid);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Removing a profile DOES NOT change the contacts on the domains using it: the registry keeps them.
Kernel::internal('client:Domains/DeleteWhoisProfile', ['owner_id' => $uid, 'pid' => $pid]);Making a Profile the Default
Sets the profile applied by itself on new registrations.
curl -X POST 'https://panel.example.com/api/v1/client/domains/whois-profiles/7/default' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/domains/whois-profiles/${pid}/default`, {
method: 'POST',
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/domains/whois-profiles/' . $pid . '/default');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The default is used on new registrations and transfers ALONE; it never reaches the domains you hold.
Kernel::internal('client:Domains/SetDefaultWhoisProfile', ['owner_id' => $uid, 'pid' => $pid]);Pitfalls
The role field on the contact write is optional, and leaving it out writes the contact into all four roles. Forgetting it while meaning to change the technical contact changes the registrant as well. The profile apply endpoint always writes all four.
The registrant is the domain's legal holder. On some extensions changing those details counts as a transfer of holder: the registry sends a confirmation e-mail and closes the domain to transfer for a while. That can follow even when you are only fixing a typo.
A saved profile is a template: applying it copies the details onto the domain there and then. Editing it later does not reach those domains, and removing it takes nothing off the registry. Apply it again to each domain to spread a change.
The state field is not required on a saved profile and is on a domain contact. Applying a profile saved without it can fail where the provider wants it. Save your profiles with the state filled in.
Privacy is free on some extensions and opens straight away, while others want an add-on bought first and refuse the call without it. Read which case you are in from the add-on block in the domain detail.
Making a profile the default applies it by itself on the registrations and transfers that follow. It touches none of the domains you hold. Apply it to each of them separately to bring the existing ones into line.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.