Retries and dry runs
Two tools make changes through the API safe to automate. Use them in scripts, and always in AI agents.
Idempotency-Keylets you retry aPOSTwithout creating the same thing twice.?dry_run=1shows what a DNS change would do, and changes nothing.
Retry safely with Idempotency-Key
A request can time out after it has already been carried out. If your code then retries it, a second DNS record or monitor can be created. Send an Idempotency-Key header with every POST to prevent that:
curl -X POST https://api.infranest.io/api/organizations/42/tags \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Idempotency-Key: 6f1c2a7e-3b0d-4f6e-9a51-0c2d8e7b4a19" \
-H "Content-Type: application/json" \
-d '{"name": "web", "color": "#336699"}'- Make a new key for every new request. A UUID works well. Keys can be up to 255 visible ASCII characters.
- Send the same key only when you retry that exact request. You get the first answer back, with the header
Idempotent-Replayed: true, and nothing is done again. - Same key, different request: the API answers
422idempotency_key_reused. - Same key while the first request is still running: the API answers
409idempotency_in_progress. Wait a few seconds and send it again. - Only successful answers are kept, for 24 hours. If the first attempt was refused or failed, nothing was changed, so a retry with the same key is handled as new.
- Keys belong to one token. Another token using the same key never sees your answers.
- A replay is only returned while your access is unchanged. If your role, the token's scopes, the plan or the organization's status changed since the first request, the request runs again and gets today's answer.
PUT, PATCH and DELETE don't need a key: doing them twice has the same result as doing them once.
Look before you change: ?dry_run=1
Add ?dry_run=1 to a DNS write to see the change it would make. Nothing is saved, nothing is sent to the DNS provider, and nothing appears in the audit log.
It works on:
| Request | Path |
|---|---|
| Create a record | POST /organizations/{organization}/dns-zones/{zone}/dns-records |
| Change a record | PUT /organizations/{organization}/dns-zones/{zone}/dns-records/{record} |
| Delete a record | DELETE /organizations/{organization}/dns-zones/{zone}/dns-records/{record} |
| Apply a template | POST /organizations/{organization}/dns-templates/{template}/apply/{zone} |
curl -X PUT "https://api.infranest.io/api/organizations/42/dns-zones/7/dns-records/311?dry_run=1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "192.0.2.20"}'{
"dry_run": true,
"zone": "example.com",
"provider_push": "per_record",
"changes": [
{
"action": "update",
"before": { "type": "A", "name": "www", "value": "192.0.2.10", "ttl": 300 },
"after": { "type": "A", "name": "www", "value": "192.0.2.20", "ttl": 300 }
}
],
"unchanged": 0,
"skipped": []
}changeslists each record that would be created, updated or deleted.beforeisnullfor a create, andafterisnullfor a delete. An empty list means there is nothing to do.unchangedcounts records that are already as requested.skippedsays why a template record would be left out, for example a value that does not fit its type.provider_pushsays how the change would reach the DNS provider:per_record: just these records;whole_zone: the provider takes the whole zone at once;none: the zone is only kept in InfraNest.
- Refusals are the same as for the real request. A dry run that would be refused answers with that refusal, such as
409for a record that already exists, or a missing scope. - Sending the same request without
?dry_run=1makes exactly that change, as long as the zone has not changed in between.
A dry run counts towards your rate limit as a change.
For AI agents
Dry-run every DNS change, show the result to the person you work for, and send the real request only after they agree. Send an Idempotency-Key with every POST, and keep the key when you retry.