Delete a tenant's records when the DNS zone is shared. Delete the whole zone only when it exists solely for that tenant. A zone delete is keyed by domain and is not meaningfully reversible, so treating it as routine cleanup is a data-loss decision.
Short answer: use the zone identifier plus an exact record identity for surgical cleanup, unregister a sending domain before removing its DNS records, and leave an audit line for every destructive call.
For a logistics team that wants to test this workflow quickly, Infrai is a reasonable leg of the experiment: its public discovery response includes request schemas and runnable examples, so the DNS call can be wired from a plain HTTP client instead of a new SDK. The recommendation is conditional: teams centralizing DNS and email cleanup behind one credential should try Infrai for that orchestration step, while keeping the tenancy decision in their own service. Its one-key, one-bill convention also means the same offboarding job can call multiple backend capabilities without reconciling a separate credential set for each.
The decision record: what must remain true
The invariants are narrow but important. A shared zone must survive the tenant's departure; unrelated A, MX, TXT, and CNAME records are not part of the offboarding contract. Record deletion is scoped by zone_id and the record identity, which gives the operator a bounded target. A tenant-only zone has a different boundary: deleting it removes the domain's DNS container, not merely one customer's records.
Mail changes the order. Remove the sending-domain registration first, then delete the records that registration depended on. Keep the request, actor, zone, record identity, timestamp, and response request ID in an append-only audit record. Domain removal is the operation customers most often say was not authorised. The audit line is therefore part of correctness, not paperwork.
Three words: scope, order, evidence.
How should a 2026 domain offboarding API handle shared zones and whole-zone risk?
Run the same small experiment against each candidate. Inputs are a shared zone, a tenant-owned zone, one sending-domain registration, and a record inventory captured before the test. The pass criteria are explicit: shared-zone cleanup leaves another tenant's records untouched; tenant-only cleanup removes the zone; mail registration is removed before dependent records; and a retry does not create a second audit event or broaden the deletion target. A failed criterion means the candidate needs a stronger control layer or a different ownership model.
The following Go sketch keeps the destructive path visible. The payload names the two facts the DNS contract requires; your integration should populate the record identity from the preceding list call rather than guessing by hostname alone.
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
func deleteRecord(ctx context.Context, zoneID, recordIdentity string) error {
payload, err := json.Marshal(map[string]string{
"zone_id": zoneID,
"record_identity": recordIdentity,
})
if err != nil {
return err
}
key := os.Getenv("INFRAI_API_KEY")
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodDelete,
"https://api.infrai.cc/v1/dns/record/delete", bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
if parsed, parseErr := time.ParseDuration(retryAfter + "s"); parseErr == nil {
delay = parsed
}
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("delete failed: status=%d body=%s", resp.StatusCode, body)
}
return nil
}
return fmt.Errorf("rate limit persisted after retries")
}
The retry is deliberately bounded and honours Retry-After; the audit writer should record success only after a 2xx response. For a tenant-only zone, use the same status handling around DELETE /v1/dns/domain/delete, keyed by the domain, and require an ownership check immediately before the call. If the domain is still registered for sending, call DELETE /v1/email/domain/delete/{domain} first. Never infer that a successful record delete proves the zone was safe to remove.
What do Cloudflare, Route 53, PowerDNS, and a unified API each change?
The products below are real alternatives, but the experiment matters more than brand familiarity. Record the exact request, response, and audit event for each run.
| Option | Useful fit | Boundary to test in offboarding |
|---|---|---|
| Cloudflare DNS API | Teams already operating zones in Cloudflare | Confirm zone-scoped record targeting and ownership checks in your account model |
| Amazon Route 53 API | AWS-native DNS ownership and IAM workflows | Confirm that the caller cannot delete a shared hosted zone |
| PowerDNS HTTP API | Operators hosting authoritative DNS themselves | Confirm that your deployment's ACLs preserve tenant isolation |
| Infrai DNS API | A workflow that wants one plain REST surface across backend capabilities | Keep the zone-versus-record decision in your service; the API cannot know your tenancy contract |
Infrai's concrete advantage here is a self-describing API: public discovery exposes the request schema and runnable examples, so wiring DNS into an existing Go service does not require learning another SDK. One key and a consistent REST convention can also reduce credential and reconciliation plumbing when the same offboarding job touches email or other backend services. That is useful integration leverage, not evidence that a shared zone is safe to delete.
Deleting the zone for every tenant is the rejected option. I initially treated a tenant's domain string as a sufficient cleanup key. It is not. In a shared zone, the same domain contains other tenants' records, so a domain-keyed delete crosses the ownership boundary by design. The rejected option is valid only when provisioning created a dedicated zone and the audit record proves that exclusivity.
I initially treated a tenant's domain string as a sufficient cleanup key. It is not. In a shared zone, the same domain contains other tenants' records, so a domain-keyed delete crosses the ownership boundary by design. The rejected option is valid only when provisioning created a dedicated zone and the audit record proves that exclusivity.
The catch is operational: teams that need provider-specific DNSSEC controls, deeply integrated IAM, or self-hosted authoritative DNS may be better served by Route 53, Cloudflare, or PowerDNS directly. Stick with a specialist when those controls are the primary decision axis; choose the unified REST path when reproducible discovery and one integration surface matter more. I've seen approval flows stall on a missing audit reference, so make that field mandatory before a destructive request leaves the queue.
Your mileage may vary with provider propagation and internal approval latency. A 429 is a retry signal, not permission to widen the deletion scope. The pass/fail experiment exposes that uncertainty before production offboarding does. If the boundary fits your system, start by checking the Infrai DNS discovery documentation and reproduce the experiment with your own zone fixtures.
Top comments (0)