Register, transfer, renew and restore domains, paid from the account credit, and read a domain’s state by name.
Every money-moving call accepts an Idempotency-Key header: a retry with the same key returns the original response (with Idempotent-Replayed: true) and never charges twice.
Get a domain by name #
GET
/reseller/domains/{name}
Returns the current state of one of your domains: status, dates,
auto-renew and whether a transfer is pending or failed. The id in the
response is the one the Domain management calls are addressed by.
Use it to poll an operation that answered 202. It reads the state
stored on this server and never contacts the registry, so it is cheap
to call.
- Authentication: bearer Token
- Rate limit: 300 req/min per account
Parameters
Path
-
namestring required
Responses
-
200The domain.
Response body
application/json-
dataobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
cURL
curl 'https://portal.supporthost.com/api/v1/reseller/domains/{name}' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/{name}');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
],
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
// Save as a .mjs file (ES module, Node 18+): it uses top-level await.
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/{name}', {
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
},
});
console.log(await response.json());
200
{
"data": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
}
}
Error responses share one format: see Errors.
Get the contacts of a domain #
GET
/reseller/domains/{name}/contacts
Every contact the domain uses at its registry, keyed by role. Which
roles exist depends on the registry: required_types lists them
(registrant always; admin, tech, billing where the registry uses
them), and contacts carries exactly those.
Read live from the registry (source: "live"). A registry this server
cannot read contacts back from answers what was stored at registration
(source: "stored"): the registrant, the other roles empty.
extra_fields are the registrant’s registry-specific fields. For an
EXISTING domain this is the authoritative set: the keys the contacts
update validates against, which can differ from
GET /reseller/tlds/{tld}/extra-fields — that one describes what a NEW
registration of the extension needs with its default registry
connection.
read_only names the roles the registry does not let you edit on this
domain, with the reason (for example, an .it natural person’s admin
contact is the registrant). A read-only registrant is still sent to the
registry by the update, which then answers with the procedure it needs.
- Authentication: bearer Token
- Rate limit: 300 req/min per account
Parameters
Path
-
namestring required
Responses
-
200The contacts.
Response body
application/json-
dataobject5 properties
-
required_typesarray of stringThe roles the domain uses at its registry, in the order a form shows them.registrantis always there;admin,techandbillingdepend on the registry (an .eu domain has the registrant only, an .it one registrant, admin and tech). -
contactsmap of map of stringOne contact per role ofrequired_types:first_name,last_name,organization,street1,street2,city,state_province,postal_code,country_code,phone(+CC.NUMBER),email, empty strings when unset. A role the registry has no contact for yet is{}. -
extra_fieldsmap of stringEvery registry-specific field of the registrant, with its current value (""when none is on file): the keys the contacts update validates against for THIS domain. Authoritative for an existing domain;GET /reseller/tlds/{tld}/extra-fieldsdescribes what a new registration of the extension needs and can differ. -
read_onlymap of stringRoles the registry does not let you edit on this domain, role => reason (e.g. the admin contact of an .it natural person, which must be the registrant). Sending such a role with different data answers422 invalid_contact. Empty object when every role is editable. -
sourcestringlive: read from the registry now.stored: this domain’s registry cannot be read back, so the contacts are what was stored at registration (the registrant only).Allowed valueslivestored
-
-
-
401unauthenticatedApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disabledApiError -
404client_not_foundnot_foundApiError -
429rate_limitedApiError -
503contacts_unavailableApiError
cURL
curl 'https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
],
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts', {
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
},
});
console.log(await response.json());
200
{
"data": {
"required_types": [
"registrant",
"admin",
"tech"
],
"contacts": {
"registrant": {
"street2": "",
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"admin": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"tech": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
},
"extra_fields": {
"entity_type": "2",
"partita_iva": "01234567890",
"consent_publishing": "1",
"accept_terms": "1"
},
"read_only": [],
"source": "live"
}
}
Error responses share one format: see Errors.
Update the contacts of a domain #
PUT
/reseller/domains/{name}/contacts
Changes the roles you send, each as a full contact (the registrant
shape of POST /reseller/domains/register); roles you leave out stay
as they are. extra_fields (and source) belong to the registrant and
are accepted only with contacts.registrant; registry fields you do not
send keep the value on file. Free of charge.
Every role is validated before anything is written: a failure is
invalid_contact with fields keyed per role (contacts.admin.email,
extra_fields.codice_fiscale). A role you send whose content equals the
registry’s is not re-sent (unchanged).
Roles are applied one at a time — admin, tech, billing, then the
registrant — and the run stops at the first failure. results reports
each role sent: updated, pending (accepted, applied later: a
registrant confirming by email, say), unchanged, failed or
not_attempted. On a failure the error carries the same results, so
you know which roles were already applied.
A registrant change the registry accepts only through a
change-of-holder procedure is refused with
registrant_change_requires_trade (or
registrant_change_requires_postal_procedure): nothing is sent for the
registrant and nothing is charged.
200 with state: "completed" when every role sent is done; 202 with
state: "pending" when at least one is waiting on the registry.
- Authentication: bearer Token
- Rate limit: 30 req/min per account
Parameters
Path
-
namestring required
Body application/json
required
-
contactsobject requiredThe roles to change, keyedregistrant,admin,tech,billing. Send only roles the domain uses (required_typesofGET /reseller/domains/{name}/contacts); any other key answers422 invalid_contactnamingcontacts.<role>. Roles you leave out stay as they are.4 properties
-
registrantobjectThe owner contact, as a full contact (theregistrantofPOST /reseller/domains/register).11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | nullCompany name. Some TLDs require it and some refuse it: either case answers422 invalid_contact.-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring requiredInternational format+CC.NUMBER, e.g.+39.0212345678. You may instead send a local number here together withregistrant.phone_country_code(ISO country, e.g.IT).-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring requiredISO 3166-1 alpha-2 country code, e.g.IT.-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
adminobjectThe administrative contact, as a full contact (same fields as the registrant). Organization is always optional here.11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | null-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring required-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring required-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
techobjectThe technical contact, as a full contact.11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | null-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring required-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring required-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
billingobjectThe billing contact, as a full contact.11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | null-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring required-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring required-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
-
extra_fieldsmap of stringThe registrant’s registry-specific fields: the keysGET /reseller/domains/{name}/contactsreturns inextra_fields, which is the authoritative set for an existing domain (it can differ fromGET /reseller/tlds/{tld}/extra-fields, which describes a new registration). Only together withcontacts.registrant. Fields you do not send keep the value on file. Values are strings: send a checkbox as"1". A missing or invalid field answers422 invalid_contactnamingextra_fields.<key>. -
sourceobjectOptional, only together withcontacts.registrant. The additional domain fields of your own platform, for this server to translate intoextra_fields(seePOST /reseller/domains/register).5 properties
-
platformstringYour platform, lower case:whmcs,hostbill.-
Pattern
^[a-z][a-z0-9-]*$ -
Max length
32
-
Pattern
-
versionstring | nullYour platform’s own version, e.g.8.13.1: field names differ between versions.-
Max length
50
-
Max length
-
module_versionstring | nullVersion of the registrar module sending the request.-
Max length
50
-
Max length
-
fieldsmap of stringThe platform’s additional fields, name => value, exactly as the platform holds them (e.g."Legal Type": "Companies/one man companies"). At most 50, values up to 255 characters. -
translatedarray of stringTheextra_fieldskeys your module translated itself fromsource.fields. This server’s own translation wins over them; keys not listed here are yours and win over it.-
Max items
100 -
Max length
64
-
Max items
-
Responses
-
200Every role sent is done.
Response body
application/json-
dataobject3 properties
-
statestringcompleted(200): every role sent is done.pending(202): at least one role was accepted by the registry but applies later — typically a registrant confirming the change by email.Allowed valuescompletedpending -
resultsmap of stringOne entry per role sent, in the order they were applied:updated,pending, orunchanged(the registry already held exactly this data, so nothing was sent). The same map, withfailedandnot_attempted, rides on acontact_update_failedorregistrant_change_requires_*error so you know which roles were already applied. -
domainmap of any
-
-
-
202Accepted — completes laterAccepted; at least one role is waiting on the registry.
Response body
application/json-
dataobject3 properties
-
statestringcompleted(200): every role sent is done.pending(202): at least one role was accepted by the registry but applies later — typically a registrant confirming the change by email.Allowed valuescompletedpending -
resultsmap of stringOne entry per role sent, in the order they were applied:updated,pending, orunchanged(the registry already held exactly this data, so nothing was sent). The same map, withfailedandnot_attempted, rides on acontact_update_failedorregistrant_change_requires_*error so you know which roles were already applied. -
domainmap of any
-
-
-
401unauthenticatedApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disableddomain_not_activeApiError -
404client_not_foundnot_foundApiError -
409registrant_change_requires_traderegistrant_change_requires_postal_procedureApiError -
422invalid_requestinvalid_contactcontact_update_failedcontact_update_not_supportedApiError -
429rate_limitedApiError -
503contacts_unavailableApiError
cURL
curl -X PUT 'https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"contacts": {
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"admin": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"tech": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
},
"extra_fields": {
"entity_type": "2",
"partita_iva": "01234567890",
"consent_publishing": "1",
"accept_terms": "1"
}
}'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'contacts' => [
'registrant' => [
'first_name' => 'Mario',
'last_name' => 'Rossi',
'organization' => 'Example Srl',
'email' => 'mario.rossi@example.com',
'phone' => '+39.0212345678',
'street1' => 'Via Roma 1',
'city' => 'Milano',
'state_province' => 'MI',
'postal_code' => '20121',
'country_code' => 'IT',
],
'admin' => [
'first_name' => 'Laura',
'last_name' => 'Bianchi',
'organization' => 'Example Srl',
'email' => 'hostmaster@example.com',
'phone' => '+39.0212345679',
'street1' => 'Via Roma 1',
'street2' => '',
'city' => 'Milano',
'state_province' => 'MI',
'postal_code' => '20121',
'country_code' => 'IT',
],
'tech' => [
'first_name' => 'Laura',
'last_name' => 'Bianchi',
'organization' => 'Example Srl',
'email' => 'hostmaster@example.com',
'phone' => '+39.0212345679',
'street1' => 'Via Roma 1',
'street2' => '',
'city' => 'Milano',
'state_province' => 'MI',
'postal_code' => '20121',
'country_code' => 'IT',
],
],
'extra_fields' => [
'entity_type' => '2',
'partita_iva' => '01234567890',
'consent_publishing' => '1',
'accept_terms' => '1',
],
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/{name}/contacts', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"contacts": {
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"admin": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
},
"tech": {
"first_name": "Laura",
"last_name": "Bianchi",
"organization": "Example Srl",
"email": "hostmaster@example.com",
"phone": "+39.0212345679",
"street1": "Via Roma 1",
"street2": "",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
},
"extra_fields": {
"entity_type": "2",
"partita_iva": "01234567890",
"consent_publishing": "1",
"accept_terms": "1"
}
}),
});
console.log(await response.json());
200
{
"data": {
"state": "completed",
"results": {
"admin": "updated",
"tech": "unchanged",
"registrant": "updated"
},
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
}
}
}
202
{
"data": {
"state": "pending",
"results": {
"admin": "updated",
"registrant": "pending"
},
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
}
}
}
Error responses share one format: see Errors.
Register a domain #
POST
/reseller/domains/register
Registers an available name, paid from your credit at your net price.
Check availability and price first with GET /reseller/domains/check.
yearsdefaults to the TLD’s minimum term;GET /reseller/pricinglists the terms you can buy.nameservers(2–5 host names) is optional: omitted, the default nameservers configured on this server are used.registrantis the owner contact. The phone is+CC.NNNN(e.g.+39.0212345678), or a local number plusphone_country_code(ISO country, e.g.IT).extra_fieldsis an object of string values for the registry-specific data some TLDs require (entity type, tax codes, language…). Which keys are required depends on the TLD and on this server’s configuration, and some are only required depending on another field’s answer. Send a checkbox as"1". Keys the TLD does not declare are ignored. A missing or invalid field answers422 invalid_contact, whosefieldsmap names every field to fix.sourceis optional: your platform’s own additional fields, as your platform holds them, for this server to translate intoextra_fields. A key you send inextra_fieldswins over a translated one.
Side effects: the charge is taken from your credit before the registry
is contacted, and refunded automatically if the registry refuses. On
424 registrar_unknown the registry did not confirm the outcome and the
charge is NOT refunded while it is reconciled: do not retry with a new
Idempotency-Key.
- Authentication: bearer Token
- Rate limit: 30 req/min per account
Parameters
Header
-
Idempotency-KeystringOptional. 1–255 printable ASCII characters, scoped to your account. A retry with the same key and the same request returns the stored response byte for byte (withIdempotent-Replayed: true) and never charges twice. The same key with a different request answers422 idempotency_key_reused; while the first request is still running,409 idempotency_in_progress(retry afterretry_afterseconds). Keys are kept for 30 days. See Idempotency in the introduction.-
Min length
1 -
Max length
255
-
Min length
Body application/json
required
-
namestring requiredThe domain to register, e.g.example.com. A Unicode (IDN) name is accepted only on a TLD that supports it, and is converted to its ASCII form; otherwise422 invalid_domain.-
Max length
255
-
Max length
-
yearsintegerRegistration term. Defaults to the TLD’s minimum; must be one of theregistertermsGET /reseller/pricinglists for the TLD, otherwise422 invalid_years(withallowed_years).-
Minimum
1
-
Minimum
-
nameserversarray of string2 to 5 nameserver host names. Optional: when omitted, the default nameservers configured on this server are used.-
Min items
2 -
Max items
5 -
Pattern
^[A-Za-z0-9.\-]+$ -
Max length
255
-
Min items
-
registrantobject requiredThe owner contact of the domain, the only contact these calls take. The registrar fills the other roles the TLD needs (admin, tech, billing) on its own: most copy the registrant, some use contacts of their own. Change any role afterwards withPUT /reseller/domains/{name}/contacts.11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | nullCompany name. Some TLDs require it and some refuse it: either case answers422 invalid_contact.-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring requiredInternational format+CC.NUMBER, e.g.+39.0212345678. You may instead send a local number here together withregistrant.phone_country_code(ISO country, e.g.IT).-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring requiredISO 3166-1 alpha-2 country code, e.g.IT. When the TLD listsregistrant_countriesinGET /reseller/pricing, it must be one of them, otherwise422 invalid_contactbefore any credit is taken.-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
extra_fieldsmap of stringRegistry-specific data some TLDs require (e.g. entity type, tax codes, language). Which keys are required depends on the TLD and on this server’s configuration, and some are only required depending on the answer to another field. For a new registration or transfer,GET /reseller/tlds/{tld}/extra-fieldslists them, with their conditions; for an existing domain,GET /reseller/domains/{name}/contactsreturns the ones it uses. Values are strings: send a checkbox as"1". Keys the TLD does not declare are ignored. A missing or invalid field answers422 invalid_contactwith afieldsmap naming each field to fix. -
sourceobjectOptional. The additional domain fields of your own platform, sent as your platform holds them, for this server to translate intoextra_fields. A registrar module generated by this server fills it in: you do not need it when you sendextra_fieldsyourself. A key you send inextra_fieldsalways wins over a translated one.5 properties
-
platformstringYour platform, lower case:whmcs,hostbill.-
Pattern
^[a-z][a-z0-9-]*$ -
Max length
32
-
Pattern
-
versionstring | nullYour platform’s own version, e.g.8.13.1: field names differ between versions.-
Max length
50
-
Max length
-
module_versionstring | nullVersion of the registrar module sending the request.-
Max length
50
-
Max length
-
fieldsmap of stringThe platform’s additional fields, name => value, exactly as the platform holds them (e.g."Legal Type": "Companies/one man companies"). At most 50, values up to 255 characters. -
translatedarray of stringTheextra_fieldskeys your module translated itself fromsource.fields. This server’s own translation wins over them; keys not listed here are yours and win over it.-
Max items
100 -
Max length
64
-
Max items
-
Responses
-
200Registered.
Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject3 properties
-
statestring-
Value
completed
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
-
202Accepted — completes laterAccepted but not completed yet: the registry has taken the request and will finish it later. Your credit has already been debited; the charges stay
pendinguntil the operation completes (then they are billed on your next monthly invoice) and are refunded automatically if it fails. PollGET /reseller/domains/{name}untilstatusleavespending_registration/pending_transfer(a failed transfer showstransfer.failed: true).Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject3 properties
-
statestring-
Value
pending
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
-
401unauthenticatedApiError -
402insufficient_creditApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disabledApiError -
404client_not_foundApiError -
409operation_in_progressidempotency_in_progressApiError -
422invalid_requestinvalid_contactinvalid_domaintld_not_sellableinvalid_yearsdomain_unavailableregistration_data_missingregistrar_errorinvalid_idempotency_keyidempotency_key_reusedApiError -
424registrar_unknownApiError -
429rate_limitedApiError -
500operation_failedApiError -
503whois_unavailableApiError
cURL
# Idempotency-Key is optional: a retry with the same key is never processed twice.
curl -X POST 'https://portal.supporthost.com/api/v1/reseller/domains/register' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 00000000-0000-4000-8000-000000000000' \
-d '{
"name": "example.com",
"years": 1,
"nameservers": [
"ns1.example.net",
"ns2.example.net"
],
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
}'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/register');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key: 00000000-0000-4000-8000-000000000000',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'example.com',
'years' => 1,
'nameservers' => [
'ns1.example.net',
'ns2.example.net',
],
'registrant' => [
'first_name' => 'Mario',
'last_name' => 'Rossi',
'organization' => 'Example Srl',
'email' => 'mario.rossi@example.com',
'phone' => '+39.0212345678',
'street1' => 'Via Roma 1',
'city' => 'Milano',
'state_province' => 'MI',
'postal_code' => '20121',
'country_code' => 'IT',
],
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/register', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key': '00000000-0000-4000-8000-000000000000',
},
body: JSON.stringify({
"name": "example.com",
"years": 1,
"nameservers": [
"ns1.example.net",
"ns2.example.net"
],
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
}),
});
console.log(await response.json());
200
{
"data": {
"state": "completed",
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5310,
"action": "register",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "billable"
}
]
}
}
202
{
"data": {
"state": "pending",
"domain": {
"id": 1042,
"name": "example.com",
"status": "pending_registration",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5310,
"action": "register",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "pending"
}
]
}
}
Error responses share one format: see Errors.
Transfer a domain in #
POST
/reseller/domains/transfer
Starts the transfer of a registered name to your account, paid from your
credit at your net transfer price. The body is the register body plus
auth_code (the EPP code from the current registrar); years is not
accepted, a transfer always buys the TLD’s minimum term.
registrant, nameservers and extra_fields follow the same rules as
POST /reseller/domains/register, including the per-TLD extra_fields
and the 422 invalid_contact answer with its fields map.
Most transfers answer 202: the registry and the losing registrar take
from hours to several days to complete one. The charge is refunded
automatically if the transfer fails.
- Authentication: bearer Token
- Rate limit: 30 req/min per account
Parameters
Header
-
Idempotency-KeystringOptional. 1–255 printable ASCII characters, scoped to your account. A retry with the same key and the same request returns the stored response byte for byte (withIdempotent-Replayed: true) and never charges twice. The same key with a different request answers422 idempotency_key_reused; while the first request is still running,409 idempotency_in_progress(retry afterretry_afterseconds). Keys are kept for 30 days. See Idempotency in the introduction.-
Min length
1 -
Max length
255
-
Min length
Body application/json
required
-
namestring requiredThe domain to transfer in, e.g.example.org.-
Max length
255
-
Max length
-
nameserversarray of string2 to 5 nameserver host names. Optional: when omitted, the default nameservers configured on this server are used.-
Min items
2 -
Max items
5 -
Pattern
^[A-Za-z0-9.\-]+$ -
Max length
255
-
Min items
-
registrantobject requiredThe owner contact of the domain, the only contact these calls take. The registrar fills the other roles the TLD needs (admin, tech, billing) on its own: most copy the registrant, some use contacts of their own. Change any role afterwards withPUT /reseller/domains/{name}/contacts.11 properties
-
first_namestring required-
Max length
100
-
Max length
-
last_namestring required-
Max length
100
-
Max length
-
organizationstring | nullCompany name. Some TLDs require it and some refuse it: either case answers422 invalid_contact.-
Max length
200
-
Max length
-
emailstring required-
Format
email -
Max length
200
-
Format
-
phonestring requiredInternational format+CC.NUMBER, e.g.+39.0212345678. You may instead send a local number here together withregistrant.phone_country_code(ISO country, e.g.IT).-
Pattern
^\+[0-9]{1,3}\.[0-9]{1,14}$ -
Max length
30
-
Pattern
-
street1string required-
Max length
200
-
Max length
-
street2string | null-
Max length
200
-
Max length
-
citystring required-
Max length
100
-
Max length
-
state_provincestring | null-
Max length
100
-
Max length
-
postal_codestring required-
Max length
20
-
Max length
-
country_codestring requiredISO 3166-1 alpha-2 country code, e.g.IT. When the TLD listsregistrant_countriesinGET /reseller/pricing, it must be one of them, otherwise422 invalid_contactbefore any credit is taken.-
Min length
2 -
Max length
2
Allowed valuesAFALDZADAOAIAQAGSAAR240 more values
AMAWAUATAZBSBHBDBBBEBZBJBMBTBYBOBABWBRBNBGBFBIKHCMCACVBQCZTDCLCNCYVACOKMCDCGKPKRCRCIHRCUCWDKDMECEGSVAEEREESZETFJPHFIFRGAGMGEGSDEGHJMJPGIDJJOGRGDGLGPGUGTGGGNGQGWGYGFHTHNINIDIRIQIEISBVCXNFIMKYCCCKFKFOHMMPMHUMPNSBTCVIVGAXILITJEKZKEKGKIXKKWLALSLVLBLRLYLILTLUMKMGMWMYMVMLMTMAMQMRMUYTMXFMMDMCMNMEMSMZMMNANRNPNINENGNUNONCNZOMNLPKPWPAPGPYPEPFPLPTPRQAHKMOGBCFDORERORWRUEHKNLCMFVCBLPMWSASSMSHSNRSSCSLSGSXSYSKSISOESLKUSSSZASDSRSJSECHSTTJTWTZTFPSIOTHTLTGTKTOTTTNTRTMTVUAUGHUUYUZVUVEVNWFYEZMZW -
Min length
-
-
extra_fieldsmap of stringRegistry-specific data some TLDs require (e.g. entity type, tax codes, language). Which keys are required depends on the TLD and on this server’s configuration, and some are only required depending on the answer to another field. For a new registration or transfer,GET /reseller/tlds/{tld}/extra-fieldslists them, with their conditions; for an existing domain,GET /reseller/domains/{name}/contactsreturns the ones it uses. Values are strings: send a checkbox as"1". Keys the TLD does not declare are ignored. A missing or invalid field answers422 invalid_contactwith afieldsmap naming each field to fix. -
sourceobjectOptional. The additional domain fields of your own platform, sent as your platform holds them, for this server to translate intoextra_fields. A registrar module generated by this server fills it in: you do not need it when you sendextra_fieldsyourself. A key you send inextra_fieldsalways wins over a translated one.5 properties
-
platformstringYour platform, lower case:whmcs,hostbill.-
Pattern
^[a-z][a-z0-9-]*$ -
Max length
32
-
Pattern
-
versionstring | nullYour platform’s own version, e.g.8.13.1: field names differ between versions.-
Max length
50
-
Max length
-
module_versionstring | nullVersion of the registrar module sending the request.-
Max length
50
-
Max length
-
fieldsmap of stringThe platform’s additional fields, name => value, exactly as the platform holds them (e.g."Legal Type": "Companies/one man companies"). At most 50, values up to 255 characters. -
translatedarray of stringTheextra_fieldskeys your module translated itself fromsource.fields. This server’s own translation wins over them; keys not listed here are yours and win over it.-
Max items
100 -
Max length
64
-
Max items
-
-
auth_codestring requiredThe transfer authorisation (EPP) code, obtained from the domain’s current registrar. Always required, whatever the TLD: the panel does not know reliably which registries transfer without one, so it never starts a transfer without it.-
Max length
255
-
Max length
Responses
-
200Transferred.
Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject3 properties
-
statestring-
Value
completed
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
-
202Accepted — completes laterAccepted but not completed yet: the registry has taken the request and will finish it later. Your credit has already been debited; the charges stay
pendinguntil the operation completes (then they are billed on your next monthly invoice) and are refunded automatically if it fails. PollGET /reseller/domains/{name}untilstatusleavespending_registration/pending_transfer(a failed transfer showstransfer.failed: true).Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject3 properties
-
statestring-
Value
pending
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
-
401unauthenticatedApiError -
402insufficient_creditApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disabledApiError -
404client_not_foundApiError -
409operation_in_progressidempotency_in_progressApiError -
422invalid_requestinvalid_contactinvalid_domaintld_not_sellableinvalid_yearsdomain_unavailabledomain_not_registeredregistration_data_missingregistrar_errorinvalid_idempotency_keyidempotency_key_reusedApiError -
424registrar_unknownApiError -
429rate_limitedApiError -
500operation_failedApiError -
503whois_unavailableApiError
cURL
# Idempotency-Key is optional: a retry with the same key is never processed twice.
curl -X POST 'https://portal.supporthost.com/api/v1/reseller/domains/transfer' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 00000000-0000-4000-8000-000000000000' \
-d '{
"name": "example.org",
"auth_code": "kX7#p2Qz9w",
"nameservers": [
"ns1.example.net",
"ns2.example.net"
],
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
}'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/transfer');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key: 00000000-0000-4000-8000-000000000000',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'example.org',
'auth_code' => 'kX7#p2Qz9w',
'nameservers' => [
'ns1.example.net',
'ns2.example.net',
],
'registrant' => [
'first_name' => 'Mario',
'last_name' => 'Rossi',
'organization' => 'Example Srl',
'email' => 'mario.rossi@example.com',
'phone' => '+39.0212345678',
'street1' => 'Via Roma 1',
'city' => 'Milano',
'state_province' => 'MI',
'postal_code' => '20121',
'country_code' => 'IT',
],
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/transfer', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key': '00000000-0000-4000-8000-000000000000',
},
body: JSON.stringify({
"name": "example.org",
"auth_code": "kX7#p2Qz9w",
"nameservers": [
"ns1.example.net",
"ns2.example.net"
],
"registrant": {
"first_name": "Mario",
"last_name": "Rossi",
"organization": "Example Srl",
"email": "mario.rossi@example.com",
"phone": "+39.0212345678",
"street1": "Via Roma 1",
"city": "Milano",
"state_province": "MI",
"postal_code": "20121",
"country_code": "IT"
}
}),
});
console.log(await response.json());
200
{
"data": {
"state": "completed",
"domain": {
"id": 1043,
"name": "example.org",
"status": "active",
"registration_date": "2021-05-03",
"expiration_date": "2027-05-03",
"next_due_date": "2027-05-03",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5312,
"action": "transfer",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "billable"
}
]
}
}
202
{
"data": {
"state": "pending",
"domain": {
"id": 1043,
"name": "example.org",
"status": "pending_transfer",
"registration_date": null,
"expiration_date": null,
"next_due_date": null,
"auto_renew": false,
"transfer": {
"pending": true,
"failed": false
}
},
"charges": [
{
"id": 5312,
"action": "transfer",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "pending"
}
]
}
}
Error responses share one format: see Errors.
Renew a domain #
POST
/reseller/domains/{name}/renew
Adds years to an active or grace domain, paid from your credit. A
domain in grace also pays the TLD’s grace fee (a separate grace_fee
charge). A domain in redemption must be restored first
(409 restore_required).
If the domain already has an open renewal invoice for that term, your
credit pays that invoice instead of creating a charge: charges is then
empty and paid_invoice_id / paid_invoice_covers name the invoice and
the domains it renewed (an invoice can cover several of your domains;
paid_invoice_covers_truncated: true means the list was cut at 50).
- Authentication: bearer Token
- Rate limit: 30 req/min per account
Parameters
Path
-
namestring required
Header
-
Idempotency-KeystringOptional. 1–255 printable ASCII characters, scoped to your account. A retry with the same key and the same request returns the stored response byte for byte (withIdempotent-Replayed: true) and never charges twice. The same key with a different request answers422 idempotency_key_reused; while the first request is still running,409 idempotency_in_progress(retry afterretry_afterseconds). Keys are kept for 30 days. See Idempotency in the introduction.-
Min length
1 -
Max length
255
-
Min length
Body application/json
required
-
yearsinteger requiredYears to add. Must be one of therenewtermsGET /reseller/pricinglists for the TLD, otherwise422 invalid_years(withallowed_years).-
Minimum
1 -
Maximum
999
-
Minimum
Responses
-
200Renewed.
Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject6 properties
-
statestring-
Value
completed
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
paid_invoice_idinteger -
paid_invoice_coversarray of string -
paid_invoice_covers_truncatedboolean
-
-
202Accepted — completes laterAccepted but not confirmed yet.
pending: the registrar has not confirmed the operation; your credit has already been used, and its charges staypendinguntil the domain’s dates move, then they are billed (pollGET /reseller/domains/{name}and watchexpiration_date/next_due_date).not_settled: your credit was applied to an open renewal invoice that it did not fully pay, so the domain is NOT renewed yet. In neither case is anything refunded automatically: if the operation did not happen, support follows up. Do not retry with a new Idempotency-Key.Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject6 properties
-
statestringAllowed valuespendingnot_settled -
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
paid_invoice_idinteger -
paid_invoice_coversarray of string -
paid_invoice_covers_truncatedboolean
-
-
401unauthenticatedApiError -
402insufficient_creditApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disabledApiError -
404client_not_foundnot_foundApiError -
409restore_requiredrenewal_in_progressoperation_in_progressidempotency_in_progressApiError -
422invalid_requestinvalid_yearsdomain_not_renewableregistrar_errorinvalid_idempotency_keyidempotency_key_reusedApiError -
424registrar_unknownApiError -
429rate_limitedApiError
cURL
# Idempotency-Key is optional: a retry with the same key is never processed twice.
curl -X POST 'https://portal.supporthost.com/api/v1/reseller/domains/{name}/renew' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 00000000-0000-4000-8000-000000000000' \
-d '{
"years": 1
}'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/{name}/renew');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
'Content-Type: application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key: 00000000-0000-4000-8000-000000000000',
],
CURLOPT_POSTFIELDS => json_encode([
'years' => 1,
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/{name}/renew', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
'Content-Type': 'application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key': '00000000-0000-4000-8000-000000000000',
},
body: JSON.stringify({
"years": 1
}),
});
console.log(await response.json());
200
{
"data": {
"state": "completed",
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2028-09-15",
"next_due_date": "2028-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5311,
"action": "renew",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "billable"
}
],
"paid_invoice_id": 20790,
"paid_invoice_covers": [
"example.com",
"example.net"
],
"paid_invoice_covers_truncated": true
}
}
202
{
"data": {
"state": "pending",
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5311,
"action": "renew",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "pending"
}
],
"paid_invoice_id": 20790,
"paid_invoice_covers": [
"example.com",
"example.net"
],
"paid_invoice_covers_truncated": true
}
}
Error responses share one format: see Errors.
Restore a domain from redemption #
POST
/reseller/domains/{name}/restore
Recovers a domain in redemption and renews it for one year, paid from
your credit: a restore charge priced as a one-year renewal, plus the
TLD’s redemption fee as a separate redemption_fee charge.
No body. Like renew, it pays an open renewal invoice for the domain
instead when there is one (paid_invoice_* fields, empty charges).
- Authentication: bearer Token
- Rate limit: 30 req/min per account
Parameters
Path
-
namestring required
Header
-
Idempotency-KeystringOptional. 1–255 printable ASCII characters, scoped to your account. A retry with the same key and the same request returns the stored response byte for byte (withIdempotent-Replayed: true) and never charges twice. The same key with a different request answers422 idempotency_key_reused; while the first request is still running,409 idempotency_in_progress(retry afterretry_afterseconds). Keys are kept for 30 days. See Idempotency in the introduction.-
Min length
1 -
Max length
255
-
Min length
Responses
-
200Restored.
Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject6 properties
-
statestring-
Value
completed
-
Value
-
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
paid_invoice_idinteger -
paid_invoice_coversarray of string -
paid_invoice_covers_truncatedboolean
-
-
202Accepted — completes laterAccepted but not confirmed yet.
pending: the registrar has not confirmed the operation; your credit has already been used, and its charges staypendinguntil the domain’s dates move, then they are billed (pollGET /reseller/domains/{name}and watchexpiration_date/next_due_date).not_settled: your credit was applied to an open renewal invoice that it did not fully pay, so the domain is NOT renewed yet. In neither case is anything refunded automatically: if the operation did not happen, support follows up. Do not retry with a new Idempotency-Key.Idempotent-Replayedstring response headertruewhen this response is the replay of an earlier request with the same Idempotency-Key.
Response body
application/json-
dataobject6 properties
-
statestringAllowed valuespendingnot_settled -
domainobject8 properties
-
idinteger -
namestring -
statusstringAllowed valuespendingpending_registrationpending_transferactiveexpiredgraceredemptionpending_deletetransferring_awaytransferred_awaytransfer_failedcancelledfraud -
registration_datestring | null -
expiration_datestring | null -
next_due_datestring | null -
auto_renewboolean -
transferobject2 properties
-
pendingboolean -
failedboolean
-
-
-
chargesarray of object8 properties
-
idinteger -
actionstringAllowed valuesregistertransferrenewrestoregrace_feeredemption_fee -
yearsinteger -
currencystring -
netnumber -
taxnumber -
grossnumber -
statusstringAllowed valuespendingbillableinvoicedrefunded
-
-
paid_invoice_idinteger -
paid_invoice_coversarray of string -
paid_invoice_covers_truncatedboolean
-
-
401unauthenticatedApiError -
402insufficient_creditApiError -
403client_token_requiredtoken_not_api_enabledtoken_not_client_scopedclient_access_revokedclient_cancelledip_not_allowedip_whitelist_misconfiguredforbiddennot_resellercredit_disabledApiError -
404client_not_foundnot_foundApiError -
409renewal_in_progressoperation_in_progressidempotency_in_progressApiError -
422domain_not_restorableregistrar_errorinvalid_idempotency_keyidempotency_key_reusedApiError -
424registrar_unknownApiError -
429rate_limitedApiError
cURL
# Idempotency-Key is optional: a retry with the same key is never processed twice.
curl -X POST 'https://portal.supporthost.com/api/v1/reseller/domains/{name}/restore' \
-H "Authorization: Bearer $API_TOKEN" \
-H 'Accept: application/json' \
-H 'Idempotency-Key: 00000000-0000-4000-8000-000000000000'
PHP
<?php
$ch = curl_init('https://portal.supporthost.com/api/v1/reseller/domains/{name}/restore');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Accept: application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key: 00000000-0000-4000-8000-000000000000',
],
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
// Error envelope: {"error": {"code": "...", "message": "...", "fields": {...}}}
echo $status . ' ' . ($body['error']['code'] ?? '') . ': ' . ($body['error']['message'] ?? '') . "\n";
} else {
print_r($body);
}
Node.js
const response = await fetch('https://portal.supporthost.com/api/v1/reseller/domains/{name}/restore', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json',
// Optional: a retry with the same key is never processed twice.
'Idempotency-Key': '00000000-0000-4000-8000-000000000000',
},
});
console.log(await response.json());
200
{
"data": {
"state": "completed",
"domain": {
"id": 1042,
"name": "example.com",
"status": "active",
"registration_date": "2026-09-15",
"expiration_date": "2028-09-15",
"next_due_date": "2028-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5313,
"action": "restore",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "billable"
}
],
"paid_invoice_id": 20790,
"paid_invoice_covers": [
"example.com",
"example.net"
],
"paid_invoice_covers_truncated": true
}
}
202
{
"data": {
"state": "pending",
"domain": {
"id": 1042,
"name": "example.com",
"status": "redemption",
"registration_date": "2026-09-15",
"expiration_date": "2027-09-15",
"next_due_date": "2027-09-15",
"auto_renew": false,
"transfer": {
"pending": false,
"failed": false
}
},
"charges": [
{
"id": 5313,
"action": "restore",
"years": 1,
"currency": "EUR",
"net": 11.1699999999999999289457264239899814128875732421875,
"tax": 2.45999999999999996447286321199499070644378662109375,
"gross": 13.6300000000000007815970093361102044582366943359375,
"status": "pending"
}
],
"paid_invoice_id": 20790,
"paid_invoice_covers": [
"example.com",
"example.net"
],
"paid_invoice_covers_truncated": true
}
}
Error responses share one format: see Errors.