Domain Extensions

8 Aufrufe Markdown

The ten endpoints that manage the domain extensions you sell, their prices and their registration documents.

Overview

These endpoints run the domain extensions you sell: which ones are open, which registrar they are attached to, what they cost, and which documents are asked for at registration.

An extension appears in the path by its own name, not by an id. The price table has three levels: operation type, then year, then currency. The same extension can be priced one way for a five-year registration and another for a one-year renewal.

Reference

Listing the Extensions

get/api/v1/admin/products/domain/tlds
Products/GetDomainTlds admin paged

Returns the domain extensions the installation offers.

Query parameters 3
searchstringSearches the extension.
pageintDefaults to 1.
limitintDefaults to 25, maximum 100.
Response fields data[] — 7
idintId of the extension record.
extensionstringThe extension itself. This is the path segment on the other endpoints, not the id.
statusstringactive or inactive.
rankintThe display order.
modulestringThe registrar module attached. none when there is none, and then registration cannot run automatically.
auto_pricingboolWhether automatic pricing is on.
featuresobjectWhat the extension supports.
dns_manageboolThe client can manage DNS records.
forwardingboolDomain forwarding is available.
whois_privacyboolThe registrant details can be hidden.
epp_codeboolA transfer code can be obtained.
Meta 4
totalintTotal records matching the filter.
pageintThe page you are on.
limitintThe page size.
next_pageintThe next page. Zero means you are on the last one.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl -G 'https://panel.example.com/api/v1/admin/products/domain/tlds' \
  -H "Authorization: Bearer $API_KEY" \
  -d limit=100
const url = new URL('https://panel.example.com/api/v1/admin/products/domain/tlds');
url.searchParams.set('limit', '100');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
$url = 'https://panel.example.com/api/v1/admin/products/domain/tlds?' . http_build_query(['limit' => 100]);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->GetDomainTlds([], ['limit' => 100]);

Extension Detail

get/api/v1/admin/products/domain/tlds/{tld}
Products/GetDomainTld admin addressed by extension

Returns one extension with its costs and the whole price table.

Response fields data — 13
idintId of the extension record.
extensionstringThe extension itself.
statusstringactive or inactive.
rankintThe display order.
modulestringThe registrar module attached.
auto_pricingboolWhether automatic pricing is on.
featuresobjectWhat the extension supports.
dns_manageboolThe client can manage DNS records.
forwardingboolDomain forwarding is available.
whois_privacyboolThe registrant details can be hidden.
epp_codeboolA transfer code can be obtained.
min_yearsintThe shortest registration in years.
max_yearsintThe longest registration in years.
register_costfloatWhat registration costs you at the registrar.
renewal_costfloatWhat renewal costs you at the registrar.
transfer_costfloatWhat a transfer costs you at the registrar.
pricingobjectA three-level map: operation type, then year, then currency code. The innermost object carries amount, status, promotion and promotion_status.
Errors 2
not_found404No such extension.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/products/domain/tlds/com' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/com', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->GetDomainTld(['tld' => 'com']);

// The price table has three levels: type, year, currency.
$oneYear = $response['data']['pricing']['register']['1']['USD']['amount'] ?? null;
Response
{
  "data": {
    "id": 3,
    "extension": "com",
    "status": "active",
    "module": "Enom",
    "auto_pricing": false,
    "features": {
      "dns_manage": true,
      "forwarding": false,
      "whois_privacy": true,
      "epp_code": true
    },
    "min_years": 1,
    "max_years": 10,
    "register_cost": 8.5,
    "renewal_cost": 9,
    "transfer_cost": 8.5,
    "pricing": {
      "register": {
        "1": {
          "USD": {
            "amount": 12,
            "status": true,
            "promotion": 0,
            "promotion_status": false
          }
        }
      }
    }
  }
}

Adding an Extension

post/api/v1/admin/products/domain/tlds
Products/CreateDomainTld admin 201

Adds a new extension. If you like, the starting prices and the logo go in the same request.

Body 7
extensionstringrequiredThe extension. A leading dot is dropped.
registrarstringName of the registrar module. Defaults to none attached.
statusstringactive or inactive.
display_orderintThe display order.
featuresobjectWhat the extension supports.
dns_manageboolThe client can manage DNS records.
forwardingboolDomain forwarding is available.
whois_privacyboolThe registrant details can be hidden.
epp_codeboolA transfer code can be obtained.
pricingobjectA map from currency code to a {register, transfer, renewal} object. There is no year breakdown here; the detailed table goes in through the pricing endpoint.
logostringThe extension logo. A base64 data URI or a link that can be fetched.
Response fields data — 13
dataobjectThe extension created. Same shape as the detail endpoint, but a fresh extension holds no prices yet, so pricing comes back empty.
Errors 4
extension_required422extension was empty.
extension_exists422This extension already exists.
create_failed422The insert failed.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/products/domain/tlds' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"extension":"com","registrar":"Enom","status":"active","features":{"dns_manage":true,"whois_privacy":true},"pricing":{"USD":{"register":12,"transfer":12,"renewal":14}}}'
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    extension: 'com',
    registrar: 'Enom',
    status: 'active',
    features: { dns_manage: true, whois_privacy: true },
    pricing: { USD: { register: 12, transfer: 12, renewal: 14 } },
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'extension' => 'com',
        'registrar' => 'Enom',
        'status'    => 'active',
        'features'  => ['dns_manage' => true, 'whois_privacy' => true],
        'pricing'   => ['USD' => ['register' => 12, 'transfer' => 12, 'renewal' => 14]],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The price here is a one-year starting point; use the pricing endpoint for the year breakdown.
$response = Api::Products()->CreateDomainTld([
    'extension' => 'com',
    'registrar' => 'Enom',
    'pricing'   => ['USD' => ['register' => 12, 'renewal' => 14]],
]);

Deleting an Extension

delete/api/v1/admin/products/domain/tlds/{tld}
Products/DeleteDomainTld admin prices go too

Deletes the extension and its prices.

Response fields data — 2
deletedboolWhether the delete succeeded.
extensionstringThe extension that was deleted.
Errors 3
not_found404No such extension.
blocked_by_gate422The gate:domain.tld_delete hook vetoed the operation.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/domain/tlds/com' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/com', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->DeleteDomainTld(['tld' => 'com']);

Bulk Action

post/api/v1/admin/products/domain/tlds/bulk
Products/BulkDomainTlds admin two different bodies

Switches several extensions on or off, deletes them, or changes their settings.

Body 3
actionstringrequiredThe action: enable, disable, delete or change.
extensionsstring[]The extensions to touch. Needed by the first three actions.
changesobject[]A per-extension list of changes. Each element carries extension and the fields to change: the features, the module, the status, the order. Only on the change action.
Response fields data — 2
actionstringThe action that was applied.
appliedstring[]The extensions the action actually reached.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/products/domain/tlds/bulk' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"disable","extensions":["net","org"]}'
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/bulk', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ action: 'disable', extensions: ['net', 'org'] }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'action'     => 'disable',
        'extensions' => ['net', 'org'],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The 'change' action wants a list of changes, not a list of extensions.
$response = Api::Products()->BulkDomainTlds([
    'action'  => 'change',
    'changes' => [
        ['extension' => 'net', 'whois_privacy' => true],
        ['extension' => 'org', 'status' => 'inactive'],
    ],
]);

Setting the Pricing

put/api/v1/admin/products/domain/tlds/{tld}/pricing
Products/SetDomainTldPricing admin three-level table

Writes the extension's registration, transfer and renewal prices, broken down by year and currency.

Body 2
auto_pricingboolTurns automatic pricing on or off.
pricingobjectrequiredA three-level map: operation type (register, transfer, renewal), then year, then currency code. The innermost object carries status, fee, promotion_status and promotion.
Response fields data — 13
dataobjectThe extension as it now stands. Same shape as the detail endpoint: prices come back nested type, year, currency, so read the whole pricing map rather than assuming the shape you sent survived.
Errors 3
not_found404No such extension.
update_failed422The pricing could not be stored.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/products/domain/tlds/com/pricing' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"auto_pricing":false,"pricing":{"register":{"1":{"USD":{"status":true,"fee":12,"promotion_status":false,"promotion":0}}}}}'
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/com/pricing', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    auto_pricing: false,
    pricing: {
      register: {
        1: { USD: { status: true, fee: 12, promotion_status: false, promotion: 0 } },
      },
    },
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/com/pricing');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'auto_pricing' => false,
        'pricing'      => [
            'register' => [
                1 => ['USD' => ['status' => true, 'fee' => 12]],
            ],
        ],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Reading calls the field 'amount', writing calls it 'fee' - they hold the same value.
$current = Api::Products()->GetDomainTld(['tld' => 'com'])['data']['pricing'];
$amount  = $current['register']['1']['USD']['amount'];

Api::Products()->SetDomainTldPricing([
    'tld'     => 'com',
    'pricing' => [
        'register' => [1 => ['USD' => ['status' => true, 'fee' => $amount + 1]]],
    ],
]);

Extensions That Ask for Documents

get/api/v1/admin/products/domain/docs
Products/GetDomainDocsList admin

Returns the extensions that ask for documents at registration.

Query parameters 1
searchstringSearches the extension.
Response fields data[] — 1
tldstringThe extension itself.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/products/domain/docs' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/products/domain/docs', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/docs');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->GetDomainDocsList();

Reading the Document Set

get/api/v1/admin/products/domain/tlds/{tld}/docs
Products/GetDomainTldDocs admin

Returns the documents an extension asks for at registration.

Response fields data — 3
tldstringThe extension itself.
descriptionobjectA map from language code to description. Shown to the client above the document set.
docsobject[]The documents asked for.
idintDocument id.
typestringtext, file or select.
sortnumintThe sort number.
statusstringactive or inactive.
namesobjectA map from language code to document name.
optionsarrayThe values offered on the choice type.
Errors 2
not_found404No such extension.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->GetDomainTldDocs(['tld' => 'us']);

Writing the Document Set

put/api/v1/admin/products/domain/tlds/{tld}/docs
Products/SetDomainTldDocs admin full replacement

Writes the document set. What you send becomes the truth; a document missing from it is deleted.

Body 2
descriptionobjectA map from language code to description.
docsobjectrequiredA map from document key to a document object. Each carries a type, a name per language, and rules for the type: allowed extensions, maximum size, choice values. Give a new document a key you made up; for an existing one use the record's id.
Response fields data — 3
dataobjectThe document set as it now stands. Same shape as the read endpoint, so the keys you made up come back as record ids.
Errors 3
not_found404No such extension.
update_failed422The set could not be stored. A document name may be empty, an extension may be duplicated, or nothing may have changed.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"description":{"en":"Provide a valid ID."},"docs":{"n1":{"type":"text","name":{"en":"Registrant ID"}}}}'
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    description: { en: 'Provide a valid ID.' },
    docs: {
      n1: { type: 'text', name: { en: 'Registrant ID' } },
    },
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'description' => ['en' => 'Provide a valid ID.'],
        'docs'        => [
            'n1' => ['type' => 'text', 'name' => ['en' => 'Registrant ID']],
        ],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// To ADD a document, send the existing set too, or the earlier ones are deleted.
$existing = Api::Products()->GetDomainTldDocs(['tld' => 'us'])['data']['docs'];

$docs = [];
foreach ($existing as $doc) $docs[$doc['id']] = ['type' => $doc['type'], 'name' => $doc['names']];
$docs['n1'] = ['type' => 'file', 'name' => ['en' => 'Passport scan']];

Api::Products()->SetDomainTldDocs(['tld' => 'us', 'docs' => $docs]);

Deleting the Document Set

delete/api/v1/admin/products/domain/tlds/{tld}/docs
Products/DeleteDomainTldDocs admin

Deletes the extension's whole document set.

Response fields data — 2
deletedboolWhether the delete succeeded.
tldstringThe extension itself.
Errors 2
not_found404There is no document set for this extension.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/products/domain/tlds/us/docs');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Products()->DeleteDomainTldDocs(['tld' => 'us']);

Pitfalls

Reading says amount, writing says fee

In the price table the same value is called amount when you read it and fee when you write it. That means the table you read from the detail cannot go straight back: you have to rename the field first.

The document set is replaced in full

The document write does not merge. To add one document you have to send the whole existing set as well, or the earlier ones are deleted and that extension stops asking for anything. Give a new document a key you made up, and use the record's id for an existing one.

The bulk endpoint expects two different bodies

Enabling, disabling and deleting want a list of extensions, while changing settings wants a per-extension list of changes. Sending the wrong field quietly leaves the action empty: the returned list comes back empty and no error is raised. Check what was applied from that list.

An extension without a module runs by hand

With no registrar module attached the extension can still be sold, but registration, transfer and renewal do not run automatically; an operator does them by hand. Check the module is attached before opening the extension.

War das hilfreich?

Vielen Dank für Ihre Rückmeldung!

Brauchen Sie weitere Hilfe?

Unser Support-Team ist rund um die Uhr für Sie da, wenn Sie oben nicht fündig werden.