AWS Route 53 × WAB DNS Discovery

One-click enable or disable the WAB DNS Discovery TXT record on any Route 53 hosted zone. Uses the AWS Route 53 REST API via AWS SDK v2 — your credentials stay in your browser only.

Recommended: Create a dedicated IAM user with only route53:ChangeResourceRecordSets and route53:ListResourceRecordSets permissions on the specific hosted zone. Never use root account credentials.
⚠ AWS Access Key and Secret Key are used client-side only to sign requests directly to Route 53. They are never sent to WAB servers. Use temporary STS credentials (AssumeRole) when possible.

1. AWS Credentials

2. Hosted Zone & Domain

3. Actions

How it works

1
Fetch the WAB record template (GET /api/discovery/provider/record-template) to get TXT value: v=wab1; endpoint=https://…
2
If no Zone ID provided, call Route 53 ListHostedZonesByName to resolve it from the domain name.
3
Enable: call ChangeResourceRecordSets with action UPSERT — creates or updates _wab.example.com TXT.
Disable: list existing record → call ChangeResourceRecordSets with action DELETE.
4
Confirm with /api/discovery/provider/status. Route 53 propagation is typically 60 s or less.

Code Snippets

Node.js (AWS SDK v3)
AWS CLI
Terraform
// npm install @aws-sdk/client-route-53
import { Route53Client, ChangeResourceRecordSetsCommand, ListHostedZonesByNameCommand, ListResourceRecordSetsCommand } from '@aws-sdk/client-route-53';

const client = new Route53Client({ region: 'us-east-1' });
const DOMAIN   = 'example.com';
const ENDPOINT = `https://${DOMAIN}/.well-known/wab.json`;
const TXT_VAL  = `"v=wab1; endpoint=${ENDPOINT}"`;  // Route 53 requires double-quoted TXT

async function getZoneId() {
  const r = await client.send(new ListHostedZonesByNameCommand({ DNSName: DOMAIN, MaxItems: '1' }));
  const zone = r.HostedZones.find(z => z.Name === `${DOMAIN}.`);
  if (!zone) throw new Error(`No hosted zone found for ${DOMAIN}`);
  return zone.Id.replace('/hostedzone/', '');
}

async function getCurrentRecord(zoneId) {
  const r = await client.send(new ListResourceRecordSetsCommand({
    HostedZoneId: zoneId, StartRecordName: `_wab.${DOMAIN}`, StartRecordType: 'TXT', MaxItems: '1'
  }));
  return r.ResourceRecordSets.find(rr => rr.Name === `_wab.${DOMAIN}.` && rr.Type === 'TXT') || null;
}

async function enableWAB() {
  const zoneId = await getZoneId();
  await client.send(new ChangeResourceRecordSetsCommand({
    HostedZoneId: zoneId,
    ChangeBatch: {
      Changes: [{ Action: 'UPSERT', ResourceRecordSet: {
        Name: `_wab.${DOMAIN}`, Type: 'TXT', TTL: 3600,
        ResourceRecords: [{ Value: TXT_VAL }]
      }}]
    }
  }));
  console.log('WAB Discovery ENABLED (UPSERT applied)');
}

async function disableWAB() {
  const zoneId   = await getZoneId();
  const existing = await getCurrentRecord(zoneId);
  if (!existing) { console.log('Already disabled.'); return; }
  await client.send(new ChangeResourceRecordSetsCommand({
    HostedZoneId: zoneId,
    ChangeBatch: { Changes: [{ Action: 'DELETE', ResourceRecordSet: existing }] }
  }));
  console.log('WAB Discovery DISABLED (record deleted)');
}

enableWAB().catch(console.error);
# Set credentials
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_DEFAULT_REGION=us-east-1

# 1. Get Hosted Zone ID
ZONE_ID=$(aws route53 list-hosted-zones-by-name \
  --dns-name example.com --query "HostedZones[0].Id" --output text | cut -d/ -f3)

# 2. Enable (UPSERT)
aws route53 change-resource-record-sets \
  --hosted-zone-id "$ZONE_ID" \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name":  "_wab.example.com",
        "Type":  "TXT",
        "TTL":   3600,
        "ResourceRecords": [{"Value": "\"v=wab1; endpoint=https://example.com/.well-known/wab.json\""}]
      }
    }]
  }'

# 3. Disable (DELETE) — first fetch current record to match exactly
CURRENT_VALUE=$(aws route53 list-resource-record-sets \
  --hosted-zone-id "$ZONE_ID" \
  --query "ResourceRecordSets[?Name=='_wab.example.com.'].ResourceRecords[0].Value" \
  --output text)

aws route53 change-resource-record-sets \
  --hosted-zone-id "$ZONE_ID" \
  --change-batch "{
    \"Changes\": [{
      \"Action\": \"DELETE\",
      \"ResourceRecordSet\": {
        \"Name\":  \"_wab.example.com\",
        \"Type\":  \"TXT\",
        \"TTL\":   3600,
        \"ResourceRecords\": [{\"Value\": \"$CURRENT_VALUE\"}]
      }
    }]
  }"
# terraform — manages _wab TXT record declaratively
data "aws_route53_zone" "zone" {
  name = "example.com."
}

resource "aws_route53_record" "wab_discovery" {
  zone_id = data.aws_route53_zone.zone.zone_id
  name    = "_wab.example.com"
  type    = "TXT"
  ttl     = 3600
  records = ["v=wab1; endpoint=https://example.com/.well-known/wab.json"]
}

# To disable: remove the resource block and run `terraform apply`
# Or use count = 0 to toggle:
# count   = var.wab_enabled ? 1 : 0

Minimal IAM Policy

Create a dedicated IAM user / role with this policy, scoped to only the required hosted zone. Replace ZONE_ID with your actual Zone ID.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:ListResourceRecordSets"
      ],
      "Resource": "arn:aws:route53:::hostedzone/ZONE_ID"
    },
    {
      "Effect": "Allow",
      "Action": "route53:ListHostedZonesByName",
      "Resource": "*"
    }
  ]
}

← Back to Provider Onboarding · Cloudflare · cPanel · Sandbox · DNS Discovery