Skip to content

Retries and dry runs ​

Two tools make changes through the API safe to automate. Use them in scripts, and always in AI agents.

  • Idempotency-Key lets you retry a POST without creating the same thing twice.
  • ?dry_run=1 shows 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:

sh
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:

RequestPath
Create a recordPOST /organizations/{organization}/dns-zones/{zone}/dns-records
Change a recordPUT /organizations/{organization}/dns-zones/{zone}/dns-records/{record}
Delete a recordDELETE /organizations/{organization}/dns-zones/{zone}/dns-records/{record}
Apply a templatePOST /organizations/{organization}/dns-templates/{template}/apply/{zone}
sh
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"}'
json
{
  "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": []
}
  • changes lists each record that would be created, updated or deleted. before is null for a create, and after is null for a delete. An empty list means there is nothing to do.
  • unchanged counts records that are already as requested.
  • skipped says why a template record would be left out, for example a value that does not fit its type.
  • provider_push says 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 409 for a record that already exists, or a missing scope.
  • Sending the same request without ?dry_run=1 makes 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.