Exchange is excluded from this API/CLI release. Any historical Exchange API or CLI example in this guide is unavailable; native portal behavior is outside this exclusion. See current release scope.
DNS changes and recovery
The portal saves an original DNS change before a write goes out to the DNS provider. The records and version history in the portal are updated together once the required provider status is confirmed. With remote name servers, a change only updates local records and history; the portal does not send those records to the external provider.
Normal record changes: one command
These one-command examples require CLI 0.1.0-preview.20261102.4 or newer. Get the current signed CLI from the download page.
Create, update and delete a normal DNS record directly; a separate review/apply command is unnecessary. The portal internally creates the exact plan and runs its existing canonical writer under one original API operation and idempotency key. All current company/resource permissions, source binding, ETag checks and durable provider markers remain in place. This adds no new human-only or passkey requirement to normal record writes.
binadit dns records create example.test --name _verification --type TXT --value example --ttl 3600 --account 123 --yes
binadit dns records update example.test 12 --name _verification --type TXT --value changed --ttl 3600 --account 123 --yes
binadit dns records delete example.test 12 --account 123 --yes
# Optional validation only: no saved plan, operation or provider write.
binadit dns records create example.test --name www --type A --value 192.0.2.1 --account 123 --dry-run
When --version is omitted, the CLI reads the current records/ETag first, using dns.records:read. A caller with only dns.records:write can supply its known current --version and skip that read. An explicit stale version fails; it is never silently replaced. Normal terminal confirmation uses the existing write convention; --yes is the automation option.
Direct API routes start after /api/v1/accounts/{account}:
| Method and path | Input |
|---|---|
POST /dns/zones/{zone}/records |
name, type, value, optional ttl, priority, weight, port, comment, proxied |
PATCH /dns/zones/{zone}/records/{record} |
Changed record fields only; omitted fields retain their current values |
DELETE /dns/zones/{zone}/records/{record} |
Empty object |
All three use dns.records:write, the current zone If-Match, and the original Idempotency-Key. Optional dry_run:true validates with no allocation/provider write. The API chooses the action and target from the method/path; do not send action, zone or id in the body. Success is status:applied with the actual zone ID/name, record_action, requested_record_id (null for create), version, internal plan ID and DNS journal ID, plus the same original operation. For external nameservers published:false confirms local publication only. The endpoint does not invent a new record ID; list current records if you need that ID.
An uncertain response retains the original request/key and zone hold. No POST, PATCH or DELETE is automatically retried. Reusing that exact key reads the existing outcome; another key cannot bypass the held journal. The internal plan ID equals the original operation ID, so existing advanced plan status/recovery remains available after an exceptional interruption. These recovery controls are for uncertain outcomes, not a required normal review step. Batch/import/template plans remain separate advanced workflows.
Direct API examples in four languages
Reuse the existing Python, JavaScript/TypeScript, PHP or C# client. Those helpers use HTTPS with certificate verification, a bounded response and a timeout; they follow no redirects and never automatically retry a mutation. Do not copy service credentials into browser code.
Each example makes one direct request. Choose BINADIT_DNS_ACTION=create, update or delete, and explicitly set BINADIT_DRY_RUN=true for validation only or false to save. The company 123, zone example.test and existing record 12 are fictional: replace them with the exact authorized targets. Record IDs come from the current records list; a create receipt is not a new record ID.
Before the first attempt, read GET /api/v1/accounts/123/dns/zones/example.test/records with dns.records:read and preserve its exact HTTP ETag, including quotes, as BINADIT_RESOURCE_VERSION. A write-only caller can instead provide an already known current zone ETag. Durably preserve BINADIT_REQUEST_KEY and the exact method, URL, body and ETag before dispatch. Supply the key from your caller/secret manager; these examples never generate one. A different action or a change from preview to execution is a different request with its own key and then-current version. After an unknown response, keep the original inputs/key: do not fetch another ETag and automatically resubmit, change mode, or generate another key. Inspect the original operation first.
The returned helper data contains the API envelope. A preview is never reported as saved. An actual success must match the company, zone, action, requested record and succeeded original operation. provider_confirmed:false means records/history were applied only in the portal for external nameservers.
Python
from binadit import access_token, api
import os
base = "/api/v1/accounts/123/dns/zones/example.test/records"
action = os.environ["BINADIT_DNS_ACTION"] # create, update or delete: one request
method, path, body, record = {
"create": ("POST", base, {"name": "_verification", "type": "TXT", "value": "example", "ttl": 3600}, None),
"update": ("PATCH", base + "/12", {"value": "changed"}, 12),
"delete": ("DELETE", base + "/12", {}, 12),
}[action]
dry_run = {"true": True, "false": False}[os.environ["BINADIT_DRY_RUN"]]
if dry_run:
body["dry_run"] = True
version, key = os.environ["BINADIT_RESOURCE_VERSION"], os.environ["BINADIT_REQUEST_KEY"]
if not version or not key:
raise ValueError("Preserve the exact zone ETag and original request key first")
reply = api(access_token(), method, path, body, version=version, key=key)["data"]
data = reply["data"]
if dry_run:
if data.get("dry_run") is not True or data.get("action") != "dns.records." + action or data.get("account_id") != 123 or data.get("plan", {}).get("zone") != "example.test":
raise RuntimeError("Unverified preview; preserve the original request")
print("Validated only; DNS was not saved")
else:
op = reply.get("operation", {})
if (data.get("status") != "applied" or data.get("zone") != "example.test"
or data.get("record_action") != action or "requested_record_id" not in data or data.get("requested_record_id") != record
or not data.get("id") or op.get("id") != data["id"] or op.get("account_id") != 123
or op.get("action") != "dns.records." + action or op.get("request_status") != "succeeded"
or not isinstance(data.get("published"), bool)):
raise RuntimeError("Unverified result; preserve the original request")
print({"applied_in_portal": True, "provider_confirmed": data["published"]})
JavaScript / TypeScript
import { accessToken, api } from './binadit.mjs';
const base = '/api/v1/accounts/123/dns/zones/example.test/records';
const action = process.env.BINADIT_DNS_ACTION; // create, update or delete: one request
const choices = {
create: ['POST', base, { name: '_verification', type: 'TXT', value: 'example', ttl: 3600 }, null],
update: ['PATCH', base + '/12', { value: 'changed' }, 12],
delete: ['DELETE', base + '/12', {}, 12],
};
if (!Object.hasOwn(choices, action)) throw new Error('Choose create, update or delete');
const [method, path, body, record] = choices[action];
if (!['true', 'false'].includes(process.env.BINADIT_DRY_RUN)) throw new Error('Set BINADIT_DRY_RUN to true or false');
const dryRun = process.env.BINADIT_DRY_RUN === 'true';
if (dryRun) body.dry_run = true;
const version = process.env.BINADIT_RESOURCE_VERSION, key = process.env.BINADIT_REQUEST_KEY;
if (!version || !key) throw new Error('Preserve the exact zone ETag and original request key first');
const { data: reply } = await api(await accessToken(), method, path, body, { version, key });
const data = reply.data;
if (dryRun) {
if (data.dry_run !== true || data.action !== 'dns.records.' + action || data.account_id !== 123 || data.plan?.zone !== 'example.test')
throw new Error('Unverified preview; preserve the original request');
console.log('Validated only; DNS was not saved');
} else {
const op = reply.operation ?? {};
if (data.status !== 'applied' || data.zone !== 'example.test' || data.record_action !== action
|| data.requested_record_id !== record || !data.id || op.id !== data.id || op.account_id !== 123
|| op.action !== 'dns.records.' + action || op.request_status !== 'succeeded' || typeof data.published !== 'boolean')
throw new Error('Unverified result; preserve the original request');
console.log({ applied_in_portal: true, provider_confirmed: data.published });
}
PHP
<?php
require 'binadit.php';
$base = '/api/v1/accounts/123/dns/zones/example.test/records';
$action = getenv('BINADIT_DNS_ACTION'); // create, update or delete: one request
[$method, $path, $body, $record] = match ($action) {
'create' => ['POST', $base, ['name' => '_verification', 'type' => 'TXT', 'value' => 'example', 'ttl' => 3600], null],
'update' => ['PATCH', $base.'/12', ['value' => 'changed'], 12],
'delete' => ['DELETE', $base.'/12', [], 12],
default => throw new RuntimeException('Choose create, update or delete'),
};
$dryRun = match (getenv('BINADIT_DRY_RUN')) { 'true' => true, 'false' => false, default => throw new RuntimeException('Set BINADIT_DRY_RUN to true or false') };
if ($dryRun) $body['dry_run'] = true;
$version = getenv('BINADIT_RESOURCE_VERSION');
$key = getenv('BINADIT_REQUEST_KEY');
if (! $version || ! $key) throw new RuntimeException('Preserve the exact zone ETag and original request key first');
$reply = BinaditExample::api(BinaditExample::accessToken(), $method, $path, $body, $version, $key)['data'];
$data = $reply['data'];
if ($dryRun) {
if (($data['dry_run'] ?? null) !== true || ($data['action'] ?? null) !== 'dns.records.'.$action || ($data['account_id'] ?? null) !== 123 || ($data['plan']['zone'] ?? null) !== 'example.test')
throw new RuntimeException('Unverified preview; preserve the original request');
echo "Validated only; DNS was not saved\n";
} else {
$op = $reply['operation'] ?? [];
if (($data['status'] ?? null) !== 'applied' || ($data['zone'] ?? null) !== 'example.test' || ($data['record_action'] ?? null) !== $action
|| ! array_key_exists('requested_record_id', $data) || $data['requested_record_id'] !== $record || empty($data['id']) || ($op['id'] ?? null) !== $data['id'] || ($op['account_id'] ?? null) !== 123
|| ($op['action'] ?? null) !== 'dns.records.'.$action || ($op['request_status'] ?? null) !== 'succeeded' || ! is_bool($data['published'] ?? null))
throw new RuntimeException('Unverified result; preserve the original request');
echo json_encode(['applied_in_portal' => true, 'provider_confirmed' => $data['published']], JSON_THROW_ON_ERROR)."\n";
}
C#
using System.Text.Json.Nodes;
using var client = new BinaditExample();
string Required(string name) => Environment.GetEnvironmentVariable(name) is { Length: > 0 } value ? value : throw new InvalidOperationException("Preserve the original request inputs first");
var root = "/api/v1/accounts/123/dns/zones/example.test/records";
var action = Required("BINADIT_DNS_ACTION"); // create, update or delete: one request
var (method, path, body, record) = action switch {
"create" => ("POST", root, JsonNode.Parse("""{"name":"_verification","type":"TXT","value":"example","ttl":3600}""")!, (int?)null),
"update" => ("PATCH", root + "/12", JsonNode.Parse("""{"value":"changed"}""")!, (int?)12),
"delete" => ("DELETE", root + "/12", (JsonNode)new JsonObject(), (int?)12),
_ => throw new InvalidOperationException("Choose create, update or delete")
};
var dryRun = Required("BINADIT_DRY_RUN") switch { "true" => true, "false" => false, _ => throw new InvalidOperationException("Use true or false") };
if (dryRun) body["dry_run"] = true;
var result = await client.Api(await client.AccessToken(), method, path, body, version: Required("BINADIT_RESOURCE_VERSION"), key: Required("BINADIT_REQUEST_KEY"));
var reply = result.Data ?? throw new InvalidOperationException("Unverified result");
var data = reply["data"] ?? throw new InvalidOperationException("Unverified result");
if (dryRun) {
if (data["dry_run"]?.GetValue<bool>() != true || data["action"]?.GetValue<string>() != "dns.records." + action || data["account_id"]?.GetValue<int>() != 123 || data["plan"]?["zone"]?.GetValue<string>() != "example.test")
throw new InvalidOperationException("Unverified preview; preserve the original request");
Console.WriteLine("Validated only; DNS was not saved");
} else {
var op = reply["operation"];
if (data["status"]?.GetValue<string>() != "applied" || data["zone"]?.GetValue<string>() != "example.test" || data["record_action"]?.GetValue<string>() != action
|| !data.AsObject().ContainsKey("requested_record_id") || data["requested_record_id"]?.GetValue<int>() != record || string.IsNullOrEmpty(data["id"]?.GetValue<string>()) || op?["id"]?.GetValue<string>() != data["id"]?.GetValue<string>()
|| op?["account_id"]?.GetValue<int>() != 123 || op?["action"]?.GetValue<string>() != "dns.records." + action || op?["request_status"]?.GetValue<string>() != "succeeded" || data["published"] is null)
throw new InvalidOperationException("Unverified result; preserve the original request");
Console.WriteLine(new JsonObject { ["applied_in_portal"] = true, ["provider_confirmed"] = data["published"]!.GetValue<bool>() });
}
Changes from the DNS editor
Open a domain via DNS → DNS Records, check the records and save the change. The editor will send the version you viewed and its own, random change number per domain. Another user, API request or background task cannot therefore overwrite an outdated version unnoticed.
The browser only keeps the original change number in the session store. DNS contents, keys and session receipts are not stored there. If the answer is interrupted, the same number is retained. No replacement application is created to circumvent an uncertain change.
A confirmed completion means that the associated local records and history have been saved. A notification that the result requires checking means that the change cannot yet be treated as completed.
Version history and companies
A DNS snapshot remains linked to the company for which it is stored. If a domain is moved to another company, the new owner will not have access to the previous company's DNS history. The domain list, version counts, detail view and recovery preview follow the same company binding. Saving a new version only cleans up old versions from the same company.
If an answer is lost
Use Check original change. The status page only reads the local, verified proofs. Opening this page does not send a provider request or repeat any changes.
The page shows how many original provider steps have been confirmed. A change can have multiple steps, for example writing records and then informing the name servers. Then perform an explicit action:
- Check provider status without writing reads the current provider status. If it exactly matches the original change, the portal finalizes the local records and history. This action does not send a write to the provider.
- Execute uncompleted original steps is a separate permission for provider writes. This option can only perform original steps that have never been attempted. An uncertain write is not resent. The option appears when all steps already attempted have been confirmed.
For external name servers, this page shows Complete change in portal. That action only finalizes the original local records and history; it does not read or modify any external provider. No confirmed provider steps are shown for these domains.
Please check the domain and the original request before confirming any action. Both recovery actions require recent strong authentication with a passkey or authenticator code, according to your company's policy. Administrators use a passkey. A regular password check does not count as strong authentication here.
You may review and restore the same original change after re-login if you are still the same authorized person, at the same company and with the same valid login generation. A changed identity, revoked access, expired authentication, or a changed provider connection can block recovery. Saved approval does not replace current access.
What not to do if the result is uncertain
Do not create a new amendment with a different number to replace the original request. The portal keeps a block on the original domain and the canonical domain name until completion is demonstrably correct. Importing, linking another provider or changing the owner are also not allowed to circumvent this blockage.
A successful provider write that is missing an answer may require human review. When creating a Cloudflare zone or record, a missing original provider ID is not inferred from a coincidentally similar object. Lack of confirmation is not permission to recreate.
HTTP contract of the native editor
These are browser routes with normal portal login, CSRF protection, and current corporate permissions. Integrations and CLI agents use the public API and the OAuth or service key authentication described therein; they should not mimic a browser session or provider credential.
| Route | Behavior |
|---|---|
POST /module_dns/dns-settings/edit |
Regular direct record change with viewed version and original UUID per domain. Scheduling uses the separate scheduling process. |
GET /module_dns/mutation-context/{zoneId} |
Current authorized domain context and version; no provider request. |
GET /module_dns/mutations/{intentId} |
HTML status page; with Accept: application/json safe local status. No provider request. |
POST /module_dns/mutations/{intentId}/reconcile |
Explicit current strong consent for provider reads and any local completion. No provider write. |
POST /module_dns/mutations/{intentId}/continue |
Separate current strong consent for never-attempted original provider steps. |
The direct editor sends dns_intent_ids[zoneId] as the UUID and dns-versions[zoneId] as the viewed resource version. The server infers the person, company, provider connection, and session evidence itself. Browser-supplied owner, provider ID, or authorization fields do not grant authority.
A successful direct change contains receipts[zoneId] with intent_id, completion_verified: true, and original_receipt: true. Only the confirmation of the exact same original number allows the browser to release its saved number.
The safe status includes intent_id for the original browser approval, id for the associated DNS journal, zone_id, state, version, zone_version, approved_version, effects, attempted_effects, confirmed_effects, completion_verified and local_records_only. It does not contain DNS content, provider keys, session hashes, or unfiltered results from a parent task. Always use intent_id for the route; the journal ID is a different number.
Both recovery routes require the current version, confirm_intent_id, confirm_zone_id and confirm_zone_version. Only reconcile accepts confirm_read_only: true. Only continue accepts confirm_continue_original: true. These are different, non-interchangeable permissions. After a change in status, first retrieve new metadata; do not repeat an action with an outdated version.
Directions for AI agents
Keep the original intent number and any operational receipts with the original work. A transport error, process stop, or HTTP conflict is not a completed change and does not trigger the creation of a new idempotency key, UUID, or vendor request. Requesting status and checking provider status are different actions: only the first is exclusively local.
Public DNS plans, connections, background tasks, and native editor actions must each maintain their own current access and original parent request. Reuse the shared DNS phases from server code; never send DNS writes directly from the CLI to a DNS service. See API manual, CLI manual and agent manual for public access. The internal technical contract is in docs/DNS-RECORD-PHASES.md.
The presence of the internal DNS stages is not in itself proof that every old DNS caller has been resolved or that a full release has already been rolled out. Release controls must separately verify the actual native, API, and worker paths and the published version.
Apply and restore an original API plan
Check records, templates and import/export
binadit dns records list example.test --account 123 --output json reads the current records and zone version. Creating a DNS-only zone requires a real internal platform administrator; DNS rights of an ordinary company member do not provide that authority. Reading templates and managing custom templates use the separate dns.templates:read and dns.templates:write scopes. Changes and deletions require the ETag from dns templates get.
Export to a new private file with, for example, binadit dns records export example.test --format json --file zone.json --account 123. JSON and CSV also store Cloudflare proxied and automatic TTL 1. BIND stores DNS record data and does not have a Cloudflare proxy field. JSON, CSV and BIND support CAA, SSHFP, TLSA, HTTPS, SVCB and NAPTR, among others. CSV uses doubled quotation marks within quoted fields; backslashes remain literal characters. Ambiguous old CSV escaping is rejected.
POST dns/zones/{zone}/import-plans accepts format (json, csv, bind) and the text in content. First, use --dry-run through the advanced CLI to review a proposal without a saved plan or provider write. An import without importable records is rejected; create an explicit deletion plan for deleting records. Applying a template starts with POST dns/zones/{zone}/template-plans and template_id. This too only makes a proposition; the separate apply with the viewed version runs it.
A multi-domain export bundle imports only the exact target zone match. If that zone is missing, the bundle will be rejected. The records from different zones are not merged. A regular record list or an export from one zone remains usable as an explicit copy to another zone; check the entire proposal for that.
Wildcard names * and *.sub and service names such as _autodiscover._tcp are valid. Labels can have a maximum of 63 characters; the full name within the actual target zone maximum 253. A saved template is checked again against that target zone when applied.
You can search with binadit api '/dns/records/search?search=tekst' --limit 10 --account 123 --output json. The limit is a maximum of 20 zones per page; --all reads on explicitly. From CLI 0.1.0-preview.20261031, limit and cursor specified directly in the URL are also retained; explicit CLI flags replace those values without duplicate query parameters.
Confirming the original plan
Create a plan with POST /api/v1/accounts/{account}/dns/change-plans and check the returned version and records. POST dns/change-plans/{plan}/apply uses the viewed plan version in If-Match and an original Idempotency-Key. Relative paths below start after /api/v1/accounts/{account}/.
The API uses the same phases as the DNS editor. It saves the original plan, actual API request, key fingerprint, principal, and provider source before a provider write leaves. Every HTTP call to the provider occurs outside of an SQL transaction. Only after confirmation are records, history, plan status, journal and the original API receipt completed together. An incorrect or lost answer saves the original journal. A new key or a new apply for that same plan must not repeat an uncertain provider write.
Paid domain registration and DNS template
After confirmed payment, the portal processes the existing registration or transfer order with the registrar. A known operational error when publishing the chosen DNS template does not prevent the paid registrar action. The portal rechecks the original order, company affiliation, provider source, current authorizations and payment status. An unpaid order or a changed/terminated consent remains blocked.
Registrar status and DNS status are separate results. Only a confirmed registration with a matching domain name will receive the status ACT; a DNS error remains visible as an open publication. The original DNS request, rejected/uncertain provider step, and saved evidence are retained. The portal does not automatically repeat that step, replace a journal or create a second order or invoice. The registration task may therefore wait after registration with a DNS warning. Do not use that warning as a reason to reorder the domain.
GET dns/change-plans/{plan} reads secure local metadata without provider request. In an applied application, plan_id is the original plan; id is the DNS journal. version belongs to the journal, zone_version to the current portal zone. These versions are not interchangeable. The response contains counts and verified progress, not original DNS content, vendor keys, or private evidence.
A current business owner can explicitly restore after strong personal verification. The original service or person principal remains in the journal and the original API receipt. Recovery requires current scopes dns.records:read and dns.records:write and sensitive access enabled. A service key cannot provide this human recovery permission.
For example, for POST dns/change-plans/{plan}/actions/reconcile the confirmation is:
{
"confirm_plan_id": "11111111-1111-4111-8111-111111111111",
"confirm_mutation_id": "22222222-2222-4222-8222-222222222222",
"confirm_zone_id": 8,
"confirm_zone_version": "HUIDIGE_ZONEVERSIE_VAN_64_HEXTEKENS",
"version": "HUIDIGE_JOURNALVERSIE_VAN_64_HEXTEKENS",
"confirm_no_supplier_mutation": true
}
Use real current versions from the metadata; the texts above are placeholders. Reconcile only reads the original provider status and can complete proven local completion. This action remains available when new API writes are disabled. For remaining steps that have never been attempted, there is a separate POST .../actions/continue. Replace only confirm_no_supplier_mutation with confirm_continue_original: true. Continue requires enabled writes and does not repeat an uncertain step. These restores use the existing original request: do not send a new idempotence key and do not use dry-run or automatic retry.
If an original request has only been prepared and no provider steps have ever been attempted, the owner can explicitly close it with POST .../actions/abandon. Use the same current plan, journal and zone confirmations, with only confirm_abandon_original: true as action confirmation. This action checks the signed absence of dispatch markers, closes plan and original receipt together, and does not send a provider request. An uncertain or already attempted write cannot be released this way. A reading error never automatically causes abandon.
The additional Rust commands are:
binadit dns change-plans get PLAN_UUID --account 123 --output json
binadit dns change-plans reconcile PLAN_UUID --account 123 --confirm-plan PLAN_UUID --confirm-mutation JOURNAL_UUID --input recovery.json --yes
binadit dns change-plans continue PLAN_UUID --account 123 --confirm-plan PLAN_UUID --confirm-mutation JOURNAL_UUID --input continuation.json --yes
binadit dns change-plans abandon PLAN_UUID --account 123 --confirm-plan PLAN_UUID --confirm-mutation JOURNAL_UUID --input abandonment.json --yes
Save confirmation files with restricted file permissions, or use trusted stdin with --input -. The CLI checks plan, journal, zone and versions before connecting. Continue also checks the current capability. The restore commands do not store a new intent, do not use a new key, or automatically retry a sent action. They are additions to the original CLI inventory; source code availability does not prove a published or controlled binary release.
Native portal: copy, restore a previous version and safely cancel
The portal uses the same original DNS journal as the editor when copying and restoring. The page first requests the current target version and the source version. The server rechecks the current company permissions and source version when it saves the original approval. Copying only inherits the approved record data; Cloudflare record identities from the source are never copied. Restores can only be done from a version of the same current customer company. An empty historical record set can also be deliberately restored.
Each action saves one original UUID in the browser session. A broken response will not automatically rewrite. The page shows the original change and retains that reference even after reloading. After a confirmed completion, the same request returns the same receipt, even if the source zone has since been changed or deleted, or the selected historical version has been cleared through normal retention periods. The server will then not reread new source content. A still unclear application requires explicit assessment; it will not become another copy or recovery attempt.
The review page lists the domain name and whether the change affects only local portal records. With current strong authentication, the same original user can only have a supplier response read out. Execution of steps that have never been sent requires a separate confirmation. For an original request never started, cancel before dispatch is also available. This does not make a supplier request, retains the control information and only releases a demonstrably unused reservation. Once a supplier step has potentially been sent, cancellation remains blocked; examine the original receipt. A cancellation confirmation will not be shown as a completed DNS change.
For other agents: the native browser routes are session and CSRF bound, not an alternative to the API. Use the API/CLI change plans above. Native copy/restore binds source_zone_id/source_version and history_id/history_version respectively to the original approval proof. Recovery always requires the original intention, current target version and explicitly chosen action. Don't make up a new UUID to bypass an unknown answer. The browser only stores references and versions; secret vendor information and record content are not added to the recovery metadata.
Native portal: search and replace multiple domains
Search and replace first shows the domains involved and numbers of records actually changed. The overview is built up in limited, local pages and does not make a supplier request. The confirmation button approves exactly these domains, search and replace text and versions. The comparison is literal and case-insensitive; regular expressions are not executed.
Each domain receives a separate initial DNS approval and receipt. Changed target versions, revoked permissions and unconfirmed vendor responses are visible per domain. The portal will not report that everything was successful until each original receipt has been verified. A partial error will remain with the original review links. There is no automatic replay and no new starting point is derived from changed records when the same original request is resubmitted.
Agents use the API change-plans with the existing search/replace function and track the original plan status with the CLI. The native session routes are not a machine API. If the answer is unclear, do not create a new application to avoid the original assessment.
Scheduled delivery: the original approval and execution
A schedule is an approval for later delivery, not proof that a DNS supplier has accepted a change. API dns schedules set, the portal schedule editor and the existing cron use the same original plan and canonical DNS journal. Creation is local only. The plan binds the actual company/zone, approved records and zone version, execution time, original connection and its scope ceiling, and the original identity generation or real portal human/context. API schedules also retain the original HTTP receipt and its authenticated provenance. These private proofs are never returned as API fields or CLI output.
Choose an explicit timezone in API execute_at, more than now and no more than 30 days ahead. The portal uses the UTC offset for the selected date, including daylight saving changes, and rejects invalid or nonexistent local times. Typed native editor/replacement fields preserve literal TXT/comment bytes, HTML-looking text, entities and whitespace; validators inspect the original values and the portal escapes their display. Delivery must start within ten minutes after the approved time. The original company, identity, permissions, provider selection/credentials and grant must still be usable. A short-lived access token may expire while the approved background connection remains usable; revocation, changed identity generation, an expired/idle grant, a changed source or changed zone prevents delivery. A portal schedule does not depend on enabling the customer API.
At due time the worker prepares and commits its original hold, then makes every supplier read/write outside SQL. The exact dispatch witness commits before a supplier write. Only confirmed supplier state publishes portal records/history and clears the pending metadata, atomically with the original delivery receipt. External nameservers use local-only record publication and never claim a supplier acknowledgement. An uncertain result, a worker killed after sending, or a failed local publication keeps the original payload/journal held. Cron never retries or silently reconciles that held execution.
Inspect and recover an original schedule
Read GET /api/v1/accounts/{account}/dns/zones/{zone}/scheduled-change. Its data.id is the original plan UUID. data.delivery provides safe journal UUID/state, current zone_version, journal version, attempted/confirmed counts, and verified completion/abandonment when available. No supplier request occurs on this status read. In the portal open DNS → Scheduled changes → Review original delivery; a held schedule opens its original receipt automatically.
A current strongly verified company owner can explicitly recover the same original, even when the original API connection has been revoked. Both current DNS read and write privileges remain required. Reconciliation and never-attempted abandonment may run with new API writes disabled; continuation needs writes enabled. The original plan, company, principal, source, HTTP receipt and journal remain unchanged. Native staff approvals retain their staff context; native staff recovery requires the same current internal administrator. A changed provider connection cannot be used to read or continue an original supplier execution. SQL-only abandonment of a proven never-attempted original does not use that replacement connection.
Use the corresponding action under /scheduled-change/actions/:
| Action | Explicit confirmation | Effect |
|---|---|---|
reconcile |
confirm_no_supplier_mutation: true |
Supplier GET only; complete proven original local publication. |
continue |
confirm_continue_original: true |
Only never-attempted remaining original steps, after all attempted steps are confirmed. |
abandon |
confirm_abandon_original: true |
SQL only, and only if no original supplier step was ever attempted. |
Each action additionally requires confirm_plan_id, confirm_mutation_id, integer confirm_zone_id, current confirm_zone_version, and current journal version. Obtain these from the latest status read. Do not attach a new Idempotency-Key, dry-run flag/header, query parameters or another plan. An attempted/unknown write cannot be abandoned, replaced, paused or cancelled through the ordinary schedule controls. A confirmed terminal journal with altered/missing parent publication evidence is refused for operator investigation; it is not reported as a successful receipt.
The Rust CLI provides four supplementary commands alongside the existing schedule commands:
binadit dns schedules get 123
binadit dns schedules review 123 --confirm-plan "$PLAN_UUID"
# Save the exact confirmations from the latest review in a private JSON file.
binadit dns schedules reconcile 123 --confirm-plan "$PLAN_UUID" \
--confirm-mutation "$JOURNAL_UUID" --input ./schedule-review.json --yes
The JSON for read-only reconciliation is:
{
"confirm_plan_id": "11111111-1111-4111-8111-111111111111",
"confirm_mutation_id": "22222222-2222-4222-8222-222222222222",
"confirm_zone_id": 123,
"confirm_zone_version": "<64 lowercase hex characters from the current zone version>",
"version": "<64 lowercase hex characters from the current journal version>",
"confirm_no_supplier_mutation": true
}
Replace the sample UUIDs and version descriptions with the actual latest values. For continue or abandon, change the command and replace the one action confirmation with its distinct field from the table. The CLI checks the exact plan, journal, numeric zone and versions, prints only safe metadata, and never allocates another local HTTP intent or automatically retries an original action. Explicit continuation checks the current server capability first. --wait, --dry-run, --all, --set and a new request key are not these recovery workflows.
Older pending schedules and cancellation
An older schedule without a verifiable original human/connection/source approval is withheld from automatic delivery. Review and explicitly create a new schedule under current authenticated authority. Never guess an original actor or edit database evidence to make an old schedule run. Unattributed pending RDATA and a previous company's pending plan are not exposed to the current tenant after a domain transfer.
Ordinary cancellation before any delivery journal exists requires the current zone version and current portal context/API write authority. This clears pending local approval metadata; it makes no claim that a historical supplier operation was undone. Once an original journal exists, inspect it and use only its explicit recovery/never-attempted abandonment controls. Browser storage retains UUIDs only, with distinct schedule and immediate-edit keys; a lost scheduling response links back to the same original receipt. An accepted local schedule receipt releases only its matching browser key and never claims that supplier delivery is complete.
DNS for domain registration and transfer
In the event of a new registration or move via the portal or the API, the order will save the currently approved DNS template, the company, the domain and the DNS connection. A later template change will not change this existing order. Existing records from a managed zone are retained; DNS at Cloudflare or an external party remains unaffected during registration. The delivery checks the current business and payment authorization again. API commands always require their original valid rights. Paid native portal orders remain bound to the original business and payment agreement.
The DNS confirmation and the progress of the same order job are stored together. An operational DNS error will not stop a paid registrar request, but will remain visible as a separate delivery warning. In the event of a lost supplier response, the system stores the same DNS mutation and the same attempt; recovery does not resend an already accepted change. Use the original order/operation for investigation and recovery, and do not create a new order or invoice. An undelivered legacy job without verifiable DNS approval requires review by support. The system does not accept a current template as a replacement for historical permission. Manually adjusting progress flags is not a recovery procedure.
There is a limited management procedure for a newly created zone for which only the subsequent record PATCH has been demonstrably rejected with HTTP 422: domains:reject-dns-records TASK MUTATION only reviews the original receipts and retrieves the current zone via the portal. Only an exactly unchanged SOA/NS baseline, the same paid order and valid current authorizations allow confirmation with the --zone-version, --journal-version and --confirm shown. Status rejected means not executed: the old request and all supplier receipts remain. The procedure does not write anything to the DNS provider or publish any records. Only then can a separately assessed normal template plan publish the records. domains:confirm-dns-template TASK COMPLETED_MUTATION --confirm can complete the original task only when another confirmed transaction has actually saved the same originally approved records. These management commands do not replace customer rights or normal API/CLI approval.
The search and replace precheck also validates the desired record values according to the same DNS rules as the save. An invalid IPv4, IPv6 or target name therefore stops during assessment before the browser window reserves a change number. The check does not change any records or send a DNS request to the supplier.
Rejected before preparing in the portal
A direct change, copy, recovery or replacement can be rejected before a DNS mutation has been prepared, for example because someone else has already changed the same zone. The portal can then show a verified receipt with status not_prepared. This means that no write to the DNS provider has been initiated for that original request number. Any previous check of supplier metadata is not a write action. It is not confirmation that the desired records have been saved.
The original application number is definitively reserved. Even a simultaneous browser request that initially did not see a receipt cannot subsequently be executed. The portal will only remove the associated temporary browser block when the server evidence matches the company, zone, action, reviewed version, and original request number. Then check the current zone and consciously approve a new change; there is no automatic retry. With bulk replacement, already confirmed zones remain saved and rejected zones are shown as not executed.
For other AI agents, an HTTP error, a missing receipt, a 404, or a network error never proves that nothing was executed. Only explicit, server-controlled not_prepared_verified: true with status not_prepared and zero effects on the exact same native request is allowed to terminate this browser block. Existing or uncertain DNS transactions retain their original receipt and recovery procedure. Scheduled actions and the API/CLI have their own approval and recovery protocols described above; do not use this native browser state as a substitute for it. Never delete or modify saved approvals to make an attempt viable again.
Company ownership when reading DNS
Customer DNS and domain lists, record searches, exports and history require the current company's unique legacy service identity. Where a resource also carries an explicit portal company ID, it must be unset or identify that same company; a conflicting ID, including zero, does not grant access through the legacy ID. A history snapshot must belong to the original company and its domain must still be owned by the current company. Local DNS templates and nameserver groups use the same ownership rule. Entries explicitly marked as global remain a shared catalog. A missing resource returns 404 without exposing its records; ask support to reconcile an inconsistent company binding rather than retrying under another company. These rules also apply to read-only API export requests and the CLI.
The native DNS settings, multiple-domain selection, scheduled overview, domain search and history screens apply these same current primary-database checks. A conflicting company stamp never exposes record data or a domain name through an older legacy-only selection. Read-only team members retain DNS viewing access while their current permission is valid. Current internal administrators retain explicit global administration access; an old role or revoked session does not preserve that access. Selecting another company is not a repair for an inconsistent ownership record.
Cloudflare connections via the API and Rust CLI
An API link preserves the original company link, domain version, chosen credential, and direction before a change goes out to Cloudflare. The domain will remain local to its old provider until the provider status and local publishing are confirmed. Credentials are not shown in responses, change metadata, or CLI output. This section describes the API actions; the existing browser buttons have their own contract.
Use PUT /api/v1/accounts/{account}/dns/zones/{zone}/connection with the viewed domain ETag in If-Match and one original Idempotency-Key. Choose explicitly:
none: Checks zone metadata only, retains local record bytes and does not create a provider zone.pull: Reads the existing Cloudflare zone and stores the exactly typed records and history in the portal. A missing zone is not created.push: Publishes the viewed local records and retains unreviewed provider records. Onlyallow_zone_create: truein this direction gives permission to create a missing zone. This also requires the credential to be linked to the exact Cloudflare account.
DELETE on the same route disconnects locally, without removing the provider zone. fallback_provider is external or binadit. POST .../connection/actions/pull uses the same original stages for an already linked Cloudflare zone.
Example of a viewed link without record sync, with a protected input file:
{"connection_id": 42, "direction": "none"}
binadit api /dns/zones/8/connection --method PUT --account 123 \
--version BEKEKEN_DOMEIN_VERSIE --from-file connection.json \
--idempotency-key DEZELFDE_OORSPRONKELIJKE_SLEUTEL --yes
An interrupted response preserves the same original request. Read GET /api/v1/accounts/{account}/dns/connection-changes/{change} with the original dns_mutation_id. This is SQL-only metadata and includes operation_id, direction, zone_version, version, completion_verified and requires_reconciliation. Reading this status does not send a provider request.
binadit dns connection-changes get OORSPRONKELIJKE_UUID --account 123
A currently authorized business owner with recent strong authentication can explicitly restore. Even when new API writes are disabled, GET-only reconciliation and SQL-only abort remain available if current read and write privileges are present. Push/pull recovery also requires dns.records:read and dns.records:write. Saved original approval is not a current authorization.
For recovery, save both viewed versions and the exact original UUID in a protected JSON file:
{
"confirm_mutation_id": "22222222-2222-4222-8222-222222222222",
"confirm_zone_id": 8,
"confirm_zone_version": "64-HEX-ACTUELE-DOMEINVERSIE",
"version": "64-HEX-OORSPRONKELIJKE-JOURNAALVERSIE",
"confirm_no_supplier_mutation": true
}
binadit dns connection-changes reconcile OORSPRONKELIJKE_UUID --account 123 \
--confirm-mutation OORSPRONKELIJKE_UUID --input recovery.json --yes
This sends one explicit POST .../connection-changes/{change}/actions/reconcile without a new request key. The supplier is only read with GET; exact confirmation can complete the local source link, records, history and original HTTP receipt together.
Use continue only for original steps that have never been performed, with confirm_continue_original: true instead of confirm_no_supplier_mutation. The CLI first checks the current continuation capability. New writes must be enabled. Use abandon with confirm_abandon_original: true only if no provider step has ever been attempted; this frees the original block in the same SQL transaction. Both commands use the same UUID, --confirm-mutation and both current versions. No new idempotency key or replacement request is created; --dry-run, --wait and auto-repeat are not allowed here.
If a zone or record create may have been accepted but the original provider ID is missing, the request remains blocked. A similar record or a zone that has now been found does not justify adoption or a second POST. Save the original data for operator investigation.
A local CLI pending request is not automatically cleared by a successful recovery request. Check the original HTTP receipt and follow original requests to explicitly confirm that local receipt. Do not manually remove the block or pending state.
If the first response is missing and the change UUID is still unknown, first retrieve the original HTTP receipt with the original key and request body retained, as described in original requests. With the HTTP operation ID confirmed there you will only find the existing change; this lookup creates nothing and does not send a provider request:
binadit api /dns/connection-changes/by-operation/OORSPRONKELIJKE_HTTP_UUID --account 123
A changed or missing original HTTP binding is rejected. After a late local receipt error, an already signed completed journal may still exist. Explicit reconciliation may then only reconfirm the exact original HTTP receipt; it does not change the domain and journal and does not send a provider request. The original error message remains an uncertain receipt until this check passes.
completion_verified confirms full API completion. parent_receipt_verified confirms the original HTTP receipt. In the event of a late receipt error, the secure status remains readable as state: requires_reconciliation, even if dns_completion_verified: true shows that the signed DNS journal itself has already been completed. Use the version and zone_version shown for explicit reconciliation; do not resend provider write.
Store Cloudflare keys and compare record sets
POST dns/connections verifies the provided Cloudflare credential and stores it encrypted for the current Binadit company. Use name, the write-only api_token, optional email for a Global API Key and optional provider_account_id: exactly 32 hexadecimal characters from the Cloudflare account. The Binadit company is determined by the server. A newly saved credential does not change a domain: the response contains domains_linked: 0 and sync_review_required: true. Read the current zone and then use the explicit link action with the zone ETag and the desired direction none, pull or push.
PATCH dns/connections/{id} and POST dns/connections/{id}/actions/verify require the current credential ETag from GET dns/connections/{id}. This ETag is different from a zone or journal version. Changing a name alone does not cause a provider request. Any changed token, email address, or Cloudflare account will be re-verified. A verification with a final invalid token stores is_active: false; an incomplete or uncertain provider response will not be treated as valid verification. Newly saved keys also use encrypted storage; an unreadable old key does not need to be released again to store a new one.
Provider verification, available zone list, and comparison only send HTTPS GET requests to Cloudflare. GET dns/connections/{id}/zones only returns names that currently belong to the current Binadit company. Local lists use limit and cursor. The provider inventory is limited to 1,000 zones; an individual comparison on 10,000 records and a total limited answer size. An overrun or incomplete page response is an error and will not be presented as a complete inventory. For a large account, use a more limited provider connection or an individual existing zone.
GET dns/zones/{zone}/connection/comparison requires the current permissions dns.connections:read and dns.records:read. The server verifies the current company, credential, provider zone, account ID, and local zone version surrounding each provider request. The comparison preserves TXT character string boundaries, TTL, proxied status, comments and other typed record fields. It does not change any records, history or provider binding. The portal uses the same bounded reading function; sensitive provider operations require recent strong authentication and the business context of the page accessed.
The general Rust commands and binadit api use these API routes. Provide a credential via a restricted input file or trusted stdin; don't put tokens in URLs, shell arguments, or history. A successful credential save does not contain a token in the response, operation output, or metadata. In case of an interrupted change, keep the same original request key and read the original API receipt; do not create a substitute application to avoid an uncertain result.
Zones Binadit manages through a platform connection
An administrator can link a zone of your company to a platform connection: a Cloudflare credential Binadit saved that belongs to no customer company. Such a zone works like a zone on Binadit DNS. The portal shows Managed via Cloudflare by Binadit. The rights are the ordinary zone record rights, not the sensitive connection rights:
| Action | Route | Rights for the zone |
|---|---|---|
| Read records | GET dns/zones/{zone}/records |
dns.records:read |
| Compare with Cloudflare | GET dns/zones/{zone}/connection/comparison |
dns.records:read |
| Change records | POST dns/change-plans, then .../apply |
dns.records:write |
| Import from Cloudflare | POST dns/zones/{zone}/connection/actions/pull |
dns.records:write and recent strong sign-in |
Changes and imports keep the zone ETag, Idempotency-Key, review, journal, history and original receipts described above. The receipt names the acting person or integration. Binadit only sends requests for that zone's Cloudflare zone ID.
The connection itself stays with Binadit. GET dns/connections does not list it, and GET, PATCH or DELETE dns/connections/{id} and its actions return 404. PUT or DELETE dns/zones/{zone}/connection on such a zone returns 403 with code dns_connection_platform_managed. You cannot link another zone to the connection. A team member with DNS read rights can only read and compare. A member without DNS rights gets 403. Another company gets 404.
Access is checked against the zone's current company and link on every request and around every Cloudflare call. If the zone moves to another company, or the administrator unlinks, rebinds or disables the connection, the next request fails without a call to Cloudflare. The CLI uses the same routes:
binadit api /dns/zones/8/connection/comparison --account 123
binadit api /dns/zones/8/connection/actions/pull --method POST --account 123 --version VIEWED_ZONE_VERSION
New read-only connection during maintenance
If API write is disabled, OAuth omits write permissions from a newly issued personal connection. After strong authentication, a current business owner can still read an original API DNS plan, API-scheduled DNS change or provider association, reconcile only with GET requests, or abandon a proven never-sent request locally. The server checks the original signed scope boundary and the current portal permissions; the new token will not be given write permissions. dns.records:read remains necessary for planning. For connections, dns.connections:read and dns.zones:read remain necessary, for push/pull also dns.records:read.
This exception only applies as long as new API writes are disabled. When writing is enabled, the usual current scopes remain required. continue always requires current token write permissions and write enabled; a read-only recovery connection cannot send a new provider step. Keep the same original plan, journal, and operation IDs, confirm current versions, and use existing restore commands; do not make a substitute change to avoid an uncertain result.
Review Cloudflare links in the portal
After saving a key, select Check and link domains for that connection. The server reads in a limited manner which Cloudflare zones match domains of the current portal company. This check does not automatically link anything and does not show domains from other companies. Duplicate provider name associations, another existing connection, or an unresolved original change should be reviewed individually first. Then select which available domains you want to link. Each selected zone receives its own initial change; records and registrar name servers are preserved with this link. The Linked label confirms the connection, not that local and remote records are the same; use the comparison for that.
On the domain page, Save, Pull, and Push have the same original request and restore rules as record changes. Save only links the source. Pull imports the current records into the portal with history. Push outputs the reviewed local records over the existing provider connection. Creating a missing external zone requires the separate explicit checkbox on push. Disconnecting keeps both the local records and the remote zone intact. Recent strong authentication and the business context of the opened page remain required even when the API is disabled.
After an uncertain or interrupted response, the browser retains the original request number and displays a link to Check original change. Open that link for status, express read-only reconciliation, or abandoning a proven never-sent change. A repeated button press does not send a second provider change for an uncertain original request. Keep the browser data until the server confirms exact completion or a signed zero-tries certificate; a save error or an oversized browser cache stops before a new request.
For API and CLI use, POST /api/v1/accounts/{account}/dns/connections/{connection}/actions/sync is a read-only proposal. Requires current dns.connections:read and dns.zones:read permissions and strong authentication. Omit new Idempotency-Key. The response contains review_required: true, domains_linked: 0, the current connection version and per appropriate zone the version, current source, availability and any blocking reason. dry_run: true doesn't even read the supplier. The CLI uses the same capabilities:
binadit api /dns/connections/4/actions/sync --method POST --account 1
Assess this proposal and implement each desired connection separately via the existing zone connection route with the exact current zone version and your own saved original request key. A sync proposal does not authorize subsequent writes, and the server rechecks company, permissions, credential, source, and zone version upon actual link.
Remove keys and assign them to a company
Deleting a Cloudflare key is a local action and does not delete any external zone or records. First disconnect each associated zone individually with its own current zone version and original request; then delete the unmodified, unlinked key with its current key version. The old detach boolean on key deletion no longer silently disconnects zones. Also, a vendor request that still needs the key will block deletion or assignment until that original request is resolved.
Only a current, strongly verified platform administrator can assign a key to a company or make it global. The server checks the displayed version and the current target company for each key. A key with existing zones may only remain with their current company; relocation to another company or global use first requires individual disconnection. The native multi-assignment is one local transaction: a rejected save of one key rolls back the entire selection. In case of a changed state after the commit, the server returns an error for investigation, without claiming that an already committed change has been reverted. API allocation keeps the same original HTTP receipt and does not re-allocate if repeated.
Republish saved zone in the portal
Restore and Rewrite All first check your current company, recent strong authentication, and the current zone version. You then approve one original publication per domain. Rewrite rejects an outdated editor version or unsaved editor changes; first reload or save the intended change via the regular editor. The proof uses the same status page and the same individual actions for checking, executing steps never performed, and canceling before a write attempt as other DNS changes.
Restore publishes the saved recordsets and retains other records with the provider. With Binadit DNS, rewrites first delete provider-only record sets and then replace the saved sets; provider-managed SOA and NS are retained unless an NS set is explicitly included locally. A conflicting combination of a CNAME with a retained other record is rejected. For Cloudflare, this action restores existing, known record IDs; other Cloudflare records are retained. A missing Cloudflare zone requires the separate connect action with explicit zone creation permission.
The DNS rebuild management action shows a current selection and requests approval before publishing per domain. Each zone has its own version and original application number. A domain without a verifiable corporate connection is skipped and reported separately; first explicitly assign it to the right company. The result distinguishes between confirmed publications, requests requiring verification and skipped domains. It does not report total success for a partially implemented selection.
A lost response may already be an accepted write. Therefore, keep the original proof. Pressing the button again does not replace an uncertain request. First check the provider status; only steps that were never attempted in that original publication can be explicitly continued afterwards. Reading status or opening a recovery page does not start publishing.
For automation and AI agents: native restore/rewrite uses the canonical original DNS journal. A rebuild is a set of individually reviewed company/zone/version-bound publications, not a new unversioned global supplier reset. Never discard pending original receipts, infer successful public propagation from private provider storage, or invent a new intent after an unknown supplier acknowledgement. Provider-only SOA/NS and unrelated Cloudflare IDs retain the preservation rules above. Creation, deletion/deactivation and registrar nameserver changes have separate lifecycle contracts; this publication workflow does not grant them.
Same publishing via API and Rust CLI
The API routes POST dns/zones/{zone}/actions/restore and .../rewrite use the same portal publishing, DNS history, and original vendor attempts. They do not write via a separate DNS client from the CLI. A normal personal connection or a real enterprise integration with dns.records:write can publish an allowed zone; zone restrictions remain in place. First read the current zone ETag. An outdated version is rejected before an operation or vendor attempt is created.
binadit dns zones get 41 --account 123
binadit dns zones actions run 41 rewrite --account 123 --version ZONE_ETAG --dry-run
Review company, zone, saved records and the difference between restore and rewrite. Then omit --dry-run for the publication in question. A preview does not read a full current vendor snapshot and does not promise public DNS propagation. If there is an uncertain response, keep the original local request and operation ID. Give a recovery connection also operations:read. If the operation ID is lost in the response, read under the same connection binadit operations list --account 123 and find the original action and time. A renewed personal login can only read the old receipt with the exactly preserved original identity, request key and request via the documented operations/original-receipt workflow. Then use GET dns/publication-changes/by-operation/{operation} to find the existing journal, or read directly the received dns_mutation_id:
binadit dns publication-changes get JOURNAL_UUID --account 123
Recovery always uses the same journal, the original HTTP operation and current versions. For example, a current business owner with recent strong verification reviews this input file; replace all sample values with the data you just read:
{
"confirm_mutation_id": "JOURNAL_UUID",
"confirm_zone_id": 41,
"confirm_zone_version": "ACTUELE_ZONE_VERSIE",
"version": "ACTUELE_JOURNAL_VERSIE",
"confirm_no_supplier_mutation": true
}
binadit dns publication-changes reconcile JOURNAL_UUID --account 123 \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
reconcile only reads the supplier and can complete proven local publication. For an original step that has never been performed, use continue with a re-evaluated file: replace confirm_no_supplier_mutation with confirm_continue_original: true. continue requests current token write permissions and API write enabled; it does not repeat an earlier write attempt. abandon uses confirm_abandon_original: true and is only allowed when no supplier transaction has been attempted.
None of these restore commands create a new operation, key, or vendor journal. Do not use manual --idempotency-key, dry-run, or query parameters. During a write pause, a new read-only personal connection to dns.records:read can use reconcile and abandon after strong authentication. The original signed write boundary and current portal permissions remain required. A different or broader connection is not permission to replace an uncertain result.
For agents: distinguish action=restore/rewrite publication receipts from connection direction=push/pull/detach receipts. Use the dns.publication-changes.* catalogue and current zone/journal versions. Only a verified signed terminal journal plus its exact original HTTP receipt establishes completion; provider storage confirmation does not establish public DNS propagation. Keep the original local pending request until its own HTTP receipt is safely acknowledged. A service original remains a real service original when a current human owner reconciles it; never invent a personal grant or replace its parent operation.
DNS publication as platform administrator
A platform manager uses its own business context and an explicit target company. The API does not log in as a customer and does not create a customer membership. Use the individual routes POST admin/dns/zones/{zone}/actions/restore and .../rewrite with target_account_id in the JSON body and the current zone ETag in If-Match. Requires a current internal administrator role, recent strong authentication, admin:write, and enabled admin, sensitive, and write features. A normal business integration cannot obtain this authority.
For example: save {"target_account_id":123} in a locally reviewed file and use for business context 900 and zone 41:
binadit api /admin/dns/zones/41/actions/rewrite --method POST --account 900 \
--from-file publication.json --version ZONE_ETAG --dry-run
Read and review the preview before performing the same intended action without --dry-run. context_account_id is the administrator’s own context; target_account_id is the company whose zone is actually published. The original HTTP operation remains in the administrative context. The DNS journal remains with the target company. Recovery preserves both boundaries:
binadit dns publication-changes get JOURNAL_UUID --account 900 --administrator
binadit dns publication-changes reconcile JOURNAL_UUID --account 900 --administrator \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
Use the same current versions and confirmation fields as above. --administrator chooses the administrator route; it doesn't change your identity or business context. The lookup is GET admin/dns/publication-changes/by-operation/{operation}. continue and abandon also support the flag. During a write pause, a new personal administrator connection to admin:read can read, perform GET-only reconciliation, or abandon a proven never-started request locally, as long as the original signed write boundary and current management permissions allow it. Continue always requires current admin:write and enabled write.
For agents: keep the real operator context distinct from the explicit target company. Use admin.dns.publication-changes.* capabilities for administrative recovery; customer publication capabilities do not authorize these routes. Retain the original operation and journal, and never manufacture customer membership, impersonation, a replacement grant or a new supplier attempt after an unknown acknowledgement.
Remove or deactivate DNS in the portal
In the DNS overview, Deactivate DNS preserves the domain registration, services, orders and billing. It clears the portal DNS records and removes a zone managed by Binadit only after the provider confirms that the exact original zone is absent. Delete removes the local DNS domain row only when it has no registration, service, billing or task binding. Neither action deletes a registrar registration or paid service. Detach a Cloudflare connection explicitly first; these actions do not delete a Cloudflare zone.
Check the displayed domain and confirm the DNS-only action using your existing valid sign-in. The browser retains one request for that company, zone and version. After an interrupted reply, the progress page automatically checks the same original request using provider reads only; it does not ask you to approve a status check or show internal identifiers as instructions. Distinct continuation can send only an original step that has never been attempted; an uncertain DELETE is never resent. An original rejected before preparation carries a signed non-dispatchable receipt; explicitly review again before a new action.
For external DNS, removal/deactivation changes only the portal data and history. Its receipt explicitly says that external-provider absence is not verified. Public DNS propagation is not confirmed by a portal receipt.
A completed deletion remains available through its original receipt even though its DNS row no longer exists. The server checks the signed original parent, completed deletion evidence and current original person's access, company context, identity generation and verification. It never treats an absent row as authorization and refuses a replacement row reusing the retired ID. Completed receipts return to the DNS overview. A lost local save retains confirmed provider evidence for GET-only recovery; do not submit a second removal.
DNS-only removal through the API and Rust CLI
DELETE dns/zones/{zone} and POST dns/zones/{zone}/actions/deactivate use the same original DNS-only removal journal as the portal. Both require the current zone ETag and dns.zones:write. Deactivation also requires confirm_domain with the exact current domain name. Deletion refuses registration, service, billing and order bindings; deactivation preserves them. Detach Cloudflare first. External DNS produces a local-only result, without claiming that the external zone is absent.
Review the current zone and use the existing zone command preview before deliberate execution:
binadit dns zones get 41 --account 123
binadit dns zones delete 41 --account 123 --version ZONE_ETAG --dry-run
Keep the original local request and operation receipt after an uncertain reply. Locate the same journal through GET dns/removal-changes/by-operation/{operation} or the returned dns_mutation_id. Recovery does not create a new request key, operation or journal:
binadit dns removal-changes get JOURNAL_UUID --account 123
binadit dns removal-changes reconcile JOURNAL_UUID --account 123 \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
The reviewed JSON has the same fields as other original DNS recovery: confirm_mutation_id, confirm_zone_id, confirm_zone_version, version, and exactly one explicit boolean confirmation. Use the values returned by get; reconcile requires confirm_no_supplier_mutation: true. It reads the provider with GET only and may finish the local publication of an already confirmed absence. It never resends an uncertain DELETE. continue requires confirm_continue_original: true, current write scope and writes enabled; it can send only a never-attempted original deletion. abandon requires confirm_abandon_original: true and no attempted provider mutation. Abandoning a completed removal is refused.
A current strongly verified company owner may perform original recovery. During a write pause, a new personal connection with dns.zones:read can read, reconcile or abandon within the signed original scope ceiling and current native rights. It cannot continue. Normal status reads also support real zone-scoped service grants. Current resource restrictions apply even to replay of a completed deletion.
After a completed deletion, zone_deleted: true is a verified historical result. Its zone_version is the original approved version because the old row is gone; preserve that value for receipt repair. A newly allocated request for the absent row is refused, as is any replacement row reusing the retired ID. Deactivation retains the row and uses its current version. Public DNS propagation remains separate from the provider's absence acknowledgement.
For AI agents: use dns.removal-changes.*, preserve the first HTTP receipt and signed journal, and distinguish removal action=delete/deactivate from publication restore/rewrite and connection direction. Never infer deletion authority from a missing row or add allow_missing, a replacement key, customer impersonation or a repeated provider DELETE. A held result stays held until explicit recovery verifies both the original DNS journal and original HTTP receipt.
DNS-only removal as a platform administrator
A platform administrator keeps their own company context and explicitly selects the customer company. Use POST admin/dns/zones/{zone}/actions/delete for an unbound DNS-only zone, or .../deactivate to preserve registration and services. Both require target_account_id and the current zone ETag. Deactivation also requires confirm_domain with the exact domain. These actions use the same portal removal journal and preserve its billing and provider safeguards; administration does not override paid-service bindings or Cloudflare detachment requirements.
For context 900, target company 123 and zone 41, save {"target_account_id":123} as a reviewed local JSON file:
binadit api /admin/dns/zones/41/actions/delete --method POST --account 900 \
--from-file deletion.json --version ZONE_ETAG --dry-run
Review the preview before deliberate execution without --dry-run. The first HTTP receipt belongs to context 900; the DNS journal belongs to company 123. No customer login, customer membership or replacement identity is created. Administrative status includes both context_account_id and target_account_id so automation can retain this distinction.
binadit dns removal-changes get JOURNAL_UUID --account 900 --administrator
binadit dns removal-changes reconcile JOURNAL_UUID --account 900 --administrator \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
Use the same reviewed versions and confirmation fields documented above. Administrative lookup is GET admin/dns/removal-changes/by-operation/{operation}. The --administrator flag also applies to continue and abandon; it selects the administrative route, without changing the authenticated person or context. Administrative capabilities use admin.dns.removal-changes.*. Customer removal capabilities do not authorize administrative recovery.
Every administrative request requires the current internal administrator role and recent strong verification in the real operator context. Creating or continuing an effect also requires current admin:write, enabled writes and enabled administrative/sensitive functions. During a write pause, a fresh personal admin:read connection may read or explicitly reconcile the original using GETs only, or abandon a proven unattempted original within its signed original scope ceiling. It cannot continue a supplier write. A completed removed-zone receipt remains readable only while the original customer binding is valid and the retired ID has no replacement row.
For AI agents: preserve the operator context, explicit target company, original HTTP receipt and signed DNS journal independently. Never invent customer membership or impersonation, infer permission from an absent zone, replace an unknown original request, or resend an uncertain provider DELETE. Provider absence and a successfully published local result do not prove public DNS propagation.
Add DNS zones in the portal
An administrator can use Add domain in the DNS overview to create DNS zones. Choose the company, enter the domain names and optionally select a template, then click Add domain once. The portal checks the current company, permissions and template in the background. There is no separate review page, JSON preview or approval checkbox. Your valid passkey login is reused; adding or saving DNS does not require another passkey confirmation. This creates DNS only; domain registration and paid services are separate actions.
Each name keeps its own request. After an interrupted connection, the portal automatically checks that same request and shows readable progress and a normal DNS record table. It does not create a replacement zone or resend an uncertain provider creation. Only steps proven never sent can be continued explicitly. Current company permissions and the original administrator's access are still checked.
An approval that has not allocated a local zone can be explicitly closed. The same original UUID then remains non-dispatchable. If a local zone already exists, use its original recovery page; the no-allocation action refuses that case. A bound creation can be cancelled only before any provider write was attempted, leaving its empty local zone explicitly external. Refreshing a status page never starts a write. A storage error or an unresolved older browser approval keeps the original on hold until its status is reviewed.
If saving the local request link was interrupted after a committed allocation, the portal automatically checks and repairs only that same signed link. This local repair neither contacts the provider nor allocates a second zone. If completion cannot be confirmed, progress remains visible with an explanation; the portal does not claim success or repeat an uncertain write.
Create DNS-only zones through the administrator API and Rust CLI
Use POST /api/v1/accounts/{context}/admin/dns/zones with domain, target_account_id and optionally template_id. The context is the administrator's own company; the target is the company that will own the DNS zone. Every request requires the actual current internal administrator and recent passkey verification. Creation requires admin:write and enabled administrative, sensitive and write functions. It never creates a customer login, membership, domain registration or paid service.
If you select a template, first read GET admin/dns/templates/{template} and retain its ETag. Supply that ETag as If-Match or CLI --version; a missing template precondition returns 428 and a changed template returns 412. Without a template, omit the version header. The new zone does not yet have an ETag. Template records are copied into the original intent; later changes or deletion of that template cannot silently change the approved records or prevent replay of a completed original.
For operator context 900 and customer company 123, save the reviewed body as creation.json:
{"domain":"example.com","target_account_id":123,"template_id":7}
binadit api /admin/dns/templates/7 --method GET --account 900
binadit dns zones create --administrator --account 900 \
--from-file creation.json --version TEMPLATE_ETAG --dry-run
The preview returns the canonical domain, template version and full record plan. It does not reserve a zone or original operation, and does not contact the provider. Review it, then deliberately execute the same command without --dry-run, adding --yes. The CLI keeps the original HTTP receipt and request key. The server commits the single local zone, signed journal and original HTTP receipt together before any supplier request. A zone already allocated locally or found at the provider is not adopted by name.
Success returns the zone id, dns_mutation_id, context_account_id and target_account_id. Keep these with the original operation receipt. An interrupted or held creation must use the same original; never submit a replacement key to work around an uncertain supplier POST. Locate it using GET admin/dns/creation-changes/by-operation/{operation} or its journal UUID:
binadit dns creation-changes get JOURNAL_UUID --account 900 --administrator
binadit dns creation-changes reconcile JOURNAL_UUID --account 900 --administrator \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
Build reconcile.json from the latest original status, using the returned zone and journal versions:
{
"confirm_mutation_id":"JOURNAL_UUID",
"confirm_zone_id":41,
"confirm_zone_version":"ZONE_VERSION_FROM_STATUS",
"version":"JOURNAL_VERSION_FROM_STATUS",
"confirm_no_supplier_mutation":true
}
Reconciliation makes provider GETs only and may repair the same original local or HTTP completion receipt. It never repeats an uncertain provider POST. A separate continue command, with confirm_continue_original: true instead of the reconcile confirmation, can deliver only original steps that have never been attempted. It requires current admin:write and enabled writes. A separate abandon command, with confirm_abandon_original: true, can close an entirely unattempted original using SQL only; its allocated empty zone remains explicitly external. It refuses any attempted provider write or completed creation.
Recovery sends no new idempotency key, If-Match, dry-run field or query parameters. Use only the one confirmation for the chosen action. During a write pause, a fresh personal admin:read connection can read, GET-only reconcile or abandon within the signed original scope ceiling and the original administrator's current rights. It cannot continue. A missing or malformed original HTTP result remains held until explicit original reconciliation verifies and repairs the receipt.
For AI agents: require the exact admin.dns.creation-changes.* capability and the explicit --administrator flag. Preserve the operator context, customer target, original operation, journal and reviewed versions independently. Customer DNS capabilities do not authorize this administrative route. Do not manufacture customer membership, replace an uncertain original, adopt an existing provider zone, or infer public DNS propagation from a successful local or supplier receipt.
Change name server groups in the portal
Open the domain's nameserver settings and choose a group. The portal shows its nameserver addresses automatically; click Save nameservers. A normal single-domain change requires no extra review checkbox. Customer-owned configurations copy the chosen nameserver list and glue addresses to that domain. Their visible names are labels, not an instruction to create or overwrite a registrar group with the same name. New portal and API configurations use unique internal keys, so an existing registrar group name cannot block the change or alter other domains. Imported/shared registrar groups retain their explicitly displayed live-group behavior; later changes to that live group can affect attached registrations.
When selecting Cloudflare nameservers, the portal explains how to keep managing DNS through Binadit and links to the Cloudflare setup guide. Nameserver delegation and the Cloudflare API connection are separate steps.
This changes the delegation at the registrar, not the DNS record contents. A truly unregistered domain without a registrar object only receives a local group choice: set the public name servers yourself at the external registrar. A registration with another supplier is not treated as a local change here. Saved vendor settings are not proof that public DNS has already been updated.
A group migration from the name server overview shows a maximum of 25 domains per page. Select the intended domains and confirm the review; each domain has its own original change. Execution stops at the first unclear result. Please review the following page separately. Changes to registrant and name servers require separate approvals; the old edit form does not perform a holder change before an unassessed name server change.
The portal uses the current valid sign-in and current domain permissions; it does not ask for the same passkey or authenticator again for this ordinary action. Internal administrators use their own business context. The separate sensitive API/CLI verification rules still apply. The browser keeps the original number. After a lost response, open View saved change. Refreshing status does not write anything to the registrar. Reconciliation reads the known original record; continue can only execute an original step that has never been sent. Canceling only releases a reservation that was demonstrably never sent. A missing server record is not proof of completion: only an explicit confirmation can permanently close that same unused number. Do not clear the browser marker to bypass an unclear result.
A legacy customer-owned local group that was wrongly treated as a registrar group can finish its same original request after the exact registrar group read proves that group is absent. Recovery must first establish current ownership, the unchanged original group and approved records, and no plan or physical effect witnesses. The first signed execution plan then records an exact per-domain copy; the original approval remains unchanged. An authentication error, timeout, generic failure or an already attempted write never permits that correction. An existing registrar group with different records is never overwritten through this legacy recovery path.
Nameserver review and execution via API and CLI
First, read the domain and available group under the real client company. GET domains/options/nameserver-groups/{group} gives data.version; regular domain GET provides the ETag. The Rust CLI saves that HTTP header as meta.etag:
umask 077
binadit domains get 41 --account 99 --output json > domain-review.json
binadit api /domains/options/nameserver-groups/7 --account 99 --output json > group-review.json
jq '.data | {nameserver_group_id:.id,nameserver_group_version:.version}' group-review.json > nameservers.json
binadit domains actions run 41 nameservers --account 99 \
--from-file nameservers.json --version "$(jq -r '.meta.etag' domain-review.json)" --dry-run
Review the delegation in the preview. Then run the same command without --dry-run and with --yes. The CLI stores the first HTTP key. The direct route is POST /api/v1/accounts/{account}/domains/{domain}/actions/nameservers, with domains:write, the domain ETag in If-Match, a durable Idempotency-Key and exactly nameserver_group_id plus nameserver_group_version. An old group version gives 412 before preparation. An exact replay uses the same key and JSON even after the action changes the domain version.
The DNS entry POST .../dns/zones/{zone}/actions/nameservers uses the same fields and core. This entry requires dns.zones:write and with an existing registration also domains:write. It does not grant registrar rights based on DNS rights alone. Local group choice returns local_configuration_only:true and registrar_confirmed:false.
Restore the original registrar request
Save the first operation.id. Find the existing journal via GET domains/changes/by-operation/{operation}; for the DNS entry it is GET dns/nameserver-changes/by-operation/{operation}. The response contains the original journal number in data.id, separate from operation_id, and the current zone_version and journal-version.
binadit api /domains/changes/by-operation/HTTP_UUID --account 99 --output json
binadit domains changes get JOURNAL_UUID --account 99 --output json > original.json
jq '.data | {confirm_mutation_id:.id,confirm_zone_id:.zone_id,confirm_zone_version:.zone_version,version,confirm_no_supplier_mutation:true}' original.json > reconcile.json
binadit domains changes reconcile JOURNAL_UUID --account 99 \
--confirm-mutation JOURNAL_UUID --input reconcile.json --yes
Replace the sample numbers and UUIDs with the real answers. For an original request via the DNS handle, use the same commands with dns nameserver-changes instead of domains changes. The subcommands are get, reconcile, continue and abandon.
Reconciliation only does vendor GETs and may restore the same local or HTTP receipt. An uncertain PUT is never retransmitted. continue asks confirm_continue_original:true; abandon asks confirm_abandon_original:true. Always use the four identification/version fields from the last get, plus only the one confirmation for the selected action. After a recovery action, read the metadata again before choosing another action. These commands do not send a new idempotency key, If-Match, query parameters, or dry-run; the CLI here does not accept --version or --from-file, but uses --input.
Recovery requires current access from a strongly verified owner; the original approval is not a substitute for current rights. During an API write pause, a new personal read connection can inspect within the original scope ceiling, perform GET-only reconciliation, or cancel a request that was never sent. Continuing requires current write permissions and enabled writes. CLI exit code 6 means the same original request is still unresolved; no replacement has been created.
Nameservers as a platform administrator
A manager keeps --account at their own approved management company and chooses the client company separately. No customer membership or customer grant is imitated. The read-only review works with admin:read, a current internal administrator and recent personal passkey verification:
binadit api /admin/domains/41/nameserver-review/99/7 \
--account 900 --output json > admin-nameserver-review.json
jq '.data | {target_account_id,nameserver_group_id,nameserver_group_version}' admin-nameserver-review.json > admin-nameservers.json
binadit api /admin/domains/41/actions/nameservers --method POST --account 900 \
--from-file admin-nameservers.json --version "$(jq -r '.data.zone_version' admin-nameserver-review.json)" --dry-run
The review route is GET /api/v1/accounts/{operator}/admin/domains/{domain}/nameserver-review/{target}/{group}. It reads only local, controlled data and returns zone_version, nameserver_group_version, both company numbers and the delegation. The ETag is the same domain version. The group must be available to the target company. An incorrect target binding does not grant access; this route does not require customer membership and does not send a vendor request.
After review, execute the last command without --dry-run and with --yes. The admin POST requests admin:write, target_account_id plus the same two group fields and the real domain ETag. Original status and recovery are under admin/domains/changes; in Rust use binadit domains changes get JOURNAL_UUID --administrator --account 900 and in recovery also --confirm-mutation JOURNAL_UUID --input FILE --yes. Read the complete existing registration actions in the registrar guide.
For agents, keep operator company, target company, original HTTP operation, journal number, and both review versions separately. Use the current domains.changes.*, dns.nameserver-changes.* or admin.domains.changes.* capability of the selected input. Don't treat a failed original operation as permission for a new key; read the existing journal and restore the same evidence. Source code and local checks do not establish production deployment or a published CLI binary.