Enable or disable the WAB DNS Discovery TXT record for any cPanel-hosted domain. Works with cPanel's UAPI and WHM API — compatible with all major shared hosting providers.
Generate an API token: cPanel → Security → Manage API Tokens → Create.
Make sure Zone Editor feature is enabled for the account.
v=wab1; endpoint=https://…ZoneEdit::fetch_zone_records to list existing TXT records for _wab._wab TXT exists → call ZoneEdit::add_zone_record; if it exists → call ZoneEdit::edit_zone_record.ZoneEdit::remove_zone_record to delete the line./api/discovery/provider/status — DNS propagation may take up to 60 s.The cPanel UAPI calls are made from your browser directly to your server on port 2083 (HTTPS). Make sure your browser can reach the cPanel port (no firewall blocking).
// npm install node-fetch@2
const fetch = require('node-fetch');
const https = require('https');
const agent = new https.Agent({ rejectUnauthorized: false }); // for self-signed cert hosts
const HOST = 'cpanel.example.com';
const PORT = 2083;
const USER = 'myuser';
const TOKEN = process.env.CPANEL_API_TOKEN; // or password
const DOMAIN = 'example.com';
const TXT_VAL = `v=wab1; endpoint=https://${DOMAIN}/.well-known/wab.json`;
function cpHeaders() {
// API Token auth (preferred)
return { Authorization: `cpanel ${USER}:${TOKEN}` };
// Password auth: use base64(user:password) with 'Basic '
}
function cpUrl(func, params = {}) {
const qs = new URLSearchParams({ domain: DOMAIN, ...params }).toString();
return `https://${HOST}:${PORT}/execute/ZoneEdit/${func}?${qs}`;
}
async function listWabRecords() {
const r = await fetch(cpUrl('fetch_zone_records', { type: 'TXT', name: `_wab.${DOMAIN}.` }), { headers: cpHeaders(), agent });
const j = await r.json();
return j.data || [];
}
async function enableWAB() {
const records = await listWabRecords();
if (records.length) {
const line = records[0].line;
const r = await fetch(cpUrl('edit_zone_record', { line, type: 'TXT', name: `_wab.${DOMAIN}.`, txtdata: TXT_VAL, ttl: 3600 }), { headers: cpHeaders(), agent });
console.log('Updated:', await r.json());
} else {
const r = await fetch(cpUrl('add_zone_record', { type: 'TXT', name: `_wab.${DOMAIN}.`, txtdata: TXT_VAL, ttl: 3600 }), { headers: cpHeaders(), agent });
console.log('Created:', await r.json());
}
}
async function disableWAB() {
const records = await listWabRecords();
if (!records.length) { console.log('Already disabled.'); return; }
const r = await fetch(cpUrl('remove_zone_record', { line: records[0].line }), { headers: cpHeaders(), agent });
console.log('Deleted:', await r.json());
}
enableWAB().catch(console.error);
# cPanel API Token auth AUTH="cpanel myuser:MY_API_TOKEN" # List existing _wab TXT records curl -sk -H "Authorization: $AUTH" \ "https://cpanel.example.com:2083/execute/ZoneEdit/fetch_zone_records?domain=example.com&type=TXT&name=_wab.example.com." # Add _wab TXT record (enable) curl -sk -H "Authorization: $AUTH" \ "https://cpanel.example.com:2083/execute/ZoneEdit/add_zone_record?domain=example.com&type=TXT&name=_wab.example.com.&txtdata=v%3Dwab1%3B%20endpoint%3Dhttps%3A%2F%2Fexample.com%2F.well-known%2Fwab.json&ttl=3600" # Remove _wab TXT record by line number (disable) # Replace LINE_NUMBER with the value returned from fetch_zone_records curl -sk -H "Authorization: $AUTH" \ "https://cpanel.example.com:2083/execute/ZoneEdit/remove_zone_record?domain=example.com&line=LINE_NUMBER"
<?php
// cPanel UAPI via PHP — run on your hosting server or via SSH
// Requires: allow_url_fopen=On or cURL
define('CP_HOST', 'cpanel.example.com');
define('CP_PORT', 2083);
define('CP_USER', 'myuser');
define('CP_TOKEN', getenv('CPANEL_API_TOKEN'));
define('DOMAIN', 'example.com');
define('TXT_VAL', 'v=wab1; endpoint=https://' . DOMAIN . '/.well-known/wab.json');
function cp_request(string $func, array $params = []): array {
$params['domain'] = DOMAIN;
$url = 'https://' . CP_HOST . ':' . CP_PORT . '/execute/ZoneEdit/' . $func . '?' . http_build_query($params);
$ctx = stream_context_create(['http' => [
'header' => 'Authorization: cpanel ' . CP_USER . ':' . CP_TOKEN,
], 'ssl' => ['verify_peer' => false]]);
return json_decode(file_get_contents($url, false, $ctx), true);
}
function enable_wab(): void {
$records = cp_request('fetch_zone_records', ['type' => 'TXT', 'name' => '_wab.' . DOMAIN . '.']);
$payload = ['type' => 'TXT', 'name' => '_wab.' . DOMAIN . '.', 'txtdata' => TXT_VAL, 'ttl' => 3600];
if (!empty($records['data'])) {
$result = cp_request('edit_zone_record', array_merge($payload, ['line' => $records['data'][0]['line']]));
echo 'Updated: ' . json_encode($result) . PHP_EOL;
} else {
$result = cp_request('add_zone_record', $payload);
echo 'Created: ' . json_encode($result) . PHP_EOL;
}
}
function disable_wab(): void {
$records = cp_request('fetch_zone_records', ['type' => 'TXT', 'name' => '_wab.' . DOMAIN . '.']);
if (empty($records['data'])) { echo 'Already disabled.' . PHP_EOL; return; }
$result = cp_request('remove_zone_record', ['line' => $records['data'][0]['line']]);
echo 'Deleted: ' . json_encode($result) . PHP_EOL;
}
enable_wab();
← Back to Provider Onboarding · Cloudflare Integration · Provider Sandbox · DNS Discovery