cPanel / WHM × WAB DNS Discovery

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.

Security note: Your cPanel username, password or API token, and server hostname are only used client-side to call cPanel's UAPI directly. They are never sent to WAB servers. Always use a dedicated cPanel API Token instead of your account password when possible.

1. cPanel Credentials

Generate an API token: cPanel → Security → Manage API Tokens → Create. Make sure Zone Editor feature is enabled for the account.

2. Domain

3. Actions

How it works

1
Fetch the WAB record template for your domain to get the exact TXT value: v=wab1; endpoint=https://…
2
Call cPanel UAPI ZoneEdit::fetch_zone_records to list existing TXT records for _wab.
3
Enable: if no _wab TXT exists → call ZoneEdit::add_zone_record; if it exists → call ZoneEdit::edit_zone_record.
Disable: call ZoneEdit::remove_zone_record to delete the line.
4
Confirm with WAB's /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).

Code Snippets

Node.js
cURL
PHP
// 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