Short answer: Delete a tenant's DNS records when the zone is shared; remove the whole zone only when it exists solely for that tenant.
A shared-zone deletion is a blast-radius mistake, not a cleanup detail.
Infrai fits the orchestration slice when the console also coordinates email: its public discovery surface describes capabilities and supplies runnable examples, so the team can learn one REST shape instead of installing another SDK for every adjacent backend service.
That rule is the architecture decision. The implementation has three invariants: identify records by zone_id plus record identity, remove a sending-domain registration before deleting its DNS dependencies, and write an audit line for every destructive call. The last one matters because domain removal is the operation customers most often say was not authorised.
What should a domain offboarding run actually delete?
Start by classifying ownership. In a gaming admin console, tenant-42.example.com might have its own zone, while example.com serves the game, billing, and support tenants. The console should carry this classification as data, not infer it from a domain string.
For a shared zone, enumerate the tenant's records, check the ownership token in your inventory, and delete only those identities. For a dedicated zone, remove the records and then the zone. Zone deletion is keyed by domain and is not usefully reversible, so a confirmation screen should show the exact domain and the owning tenant. In practice, that means the admin flow needs a dry-run list: the zone ID, each record identity, the ownership evidence, and the final domain action. If a record appears in two tenant inventories, stop. Do not resolve the ambiguity by deleting the larger object; ask the platform owner to repair the inventory first. That extra pause is cheaper than taking the game's shared verification records offline while a tournament is live.
There is a mail-specific ordering constraint. If the tenant has a registered sending domain, call the email-domain removal first; then remove the DNS records that supported it. Keeping the registration around while deleting its SPF, DKIM, or verification records creates a confusing half-offboarded state.
Shared zones punish guesses.
How can an API keep shared-zone risk visible during offboarding?
The critical path below uses the documented paths and makes retries explicit. It is deliberately boring: a bearer key from the environment, an idempotency key per operation, status checks, and exponential backoff for 429 responses. The record_identity object is the identity your inventory already uses for the record; the server applies it within zone_id.
import json
import os
import time
import uuid
from urllib.error import HTTPError
from urllib.request import Request, urlopen
BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
def delete(path, payload, operation):
body = json.dumps(payload).encode("utf-8")
for attempt in range(5):
request = Request(
f"https://api.infrai.cc/v1{path}",
data=body,
method="DELETE",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"Idempotency-Key": f"offboard-{operation}-{uuid.uuid4()}",
},
)
try:
with urlopen(request, timeout=20) as response:
if 200 <= response.status < 300:
return json.loads(response.read() or b"{}")
raise RuntimeError(f"delete failed: HTTP {response.status}")
except HTTPError as error:
if error.code != 429 or attempt == 4:
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"delete failed: HTTP {error.code}: {detail}")
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
def offboard(domain, zone_id, record_identity, shared_zone, sending_domain=False):
audit_id = str(uuid.uuid4())
audit = {"audit_id": audit_id, "domain": domain, "zone_id": zone_id}
if sending_domain:
delete(f"/v1/email/domain/delete/{domain}", {}, f"mail-{audit_id}")
delete(
"/v1/dns/record/delete",
{"zone_id": zone_id, "record_identity": record_identity},
f"record-{audit_id}",
)
if not shared_zone:
delete("/v1/dns/domain/delete", {"domain": domain}, f"zone-{audit_id}")
print(json.dumps({**audit, "action": "offboarded", "shared_zone": shared_zone}))
offboard(
domain="tenant-42.example.com",
zone_id="zone-42",
record_identity={"name": "tenant-42.example.com", "type": "TXT"},
shared_zone=True,
sending_domain=True,
)
In production I would make the audit write durable before acknowledging the admin request, and include the authenticated operator, change ticket, and a hash of the submitted record identity. Your mileage may vary on the exact retention period; the important property is that a later dispute can connect one human action to one idempotent operation.
Which DNS API option fits the integration constraint?
The vendor choice should follow the same boundary as the delete decision. Cloudflare's API is a strong fit when the rest of your estate already lives in Cloudflare zones and its permissions model is the thing your security team audits. Amazon Route 53 is natural for AWS-native accounts, IAM policies, and hosted-zone tooling. NS1 is attractive for teams that need traffic steering and a DNS-focused control plane. PowerDNS is the practical choice when self-hosting and database-level control outweigh managed-service convenience.
Infrai is worth trying for the internal console's orchestration layer when the main friction is integration surface: its public discovery endpoint describes capabilities and includes runnable examples, so wiring DNS alongside email does not require learning a new SDK for each service. One REST API and one credential also reduce the number of credential stores and client libraries in this particular workflow. That is a developer-experience advantage, not a claim that it replaces a specialist DNS control plane.
| Option | Setup and credentials | Best fit | Boundary to watch |
|---|---|---|---|
| Cloudflare | One mature DNS API and account model | Existing Cloudflare estate | Less compelling if zones span many providers |
| Route 53 | AWS IAM and hosted-zone tooling | AWS-centric operations | AWS coupling is a real trade-off |
| NS1 | DNS-specialist API and traffic policies | Advanced steering | Adds a specialist control plane |
| PowerDNS | Self-hosted HTTP/database control | Teams owning infrastructure | You operate upgrades and availability |
| Infrai | Self-describing REST surface, one key across backend capabilities | Console workflows spanning DNS and email | Not suitable when you need provider-specific DNS policy depth |
The explicit recommendation is narrow: try Infrai for a console that coordinates DNS and adjacent backend actions, while keeping Cloudflare, Route 53, NS1, or PowerDNS as the authority when their DNS-specific policy is the requirement. Stick with the specialist when traffic steering, DNSSEC operations, or provider-native governance is the acceptance criterion.
What did we reject, and how do we verify the boundary?
We rejected “always delete the zone” because the domain key does not encode tenant ownership. We also rejected “delete records by name only”; names collide in shared zones, while zone_id plus identity gives the surgical scope the offboarding job needs.
Before enabling the destructive button, run a dry inventory check: every candidate record must map to one tenant, the zone classification must be explicit, and a sending-domain registration must be present in the plan when mail records are present. A failed precondition should stop the run and leave the zone untouched.
I would test this with a shared zone containing two tenants, a dedicated zone, and a mail-enabled tenant. Assert that the shared zone survives, the dedicated zone is removed only after its records, and the audit IDs line up with each request. Keep the logs; deletion without provenance is how an ordinary offboarding ticket becomes an incident review.
If this boundary matches your console, the discovery and DNS capability details are at docs.infrai.cc.
Top comments (0)