---
title: Provision and clean up records
description: Create and persist an exact DNS plan, authorize its digest, apply it, and later build a separately authorized cleanup plan from the receipt.
seo:
  title: Provision and safely clean up DNS records
---

Use this guide after your application can reconstruct an authorized `DnsProvider.Service` and has
durable storage for plan attempts and receipts.

## Create the plan

```ts
import { Effect } from "effect";
import { Provisioning } from "domainkit";

const result =
  yield *
  Provisioning.create({
    requirements,
    target: Provisioning.Target.DiscoverFromDomain({
      domain: "mail.example.com",
    }),
  });
```

`DiscoverFromDomain` can return:

- `Resolved` with one authoritative-zone candidate and a plan;
- `SelectionRequired` when more than one authorized account can serve the domain;
- `NotFound` when none of the authorized providers can resolve a zone.

Do not guess when selection is required. Ask the user to choose a recognizable account or team, or
narrow the authorized provider context on the server.

For a provider and zone your host already resolved, use `Provisioning.Target.ExactZone` instead.

## Block or present conflicts

Inspect every operation before requesting authorization:

```ts
const conflicts = plan.operations.filter(({ _tag }) => _tag === "conflict");
```

A `conflict` is an instruction to stop or ask the user to resolve existing DNS. DomainKit never
converts it into an update or delete operation.

## Persist the attempt

Persist the encoded plan and its digest before the user approves it. Associate it with your tenant,
domain, authorized connection, expiry, and an opaque attempt identifier.

The browser should return only the digest and any operation selection the user reviewed. Reload the
complete plan from durable server storage.

## Authorize and apply

```ts
const authorization = yield * Provisioning.authorize(plan);
const receipt = yield * Provisioning.apply({ authorization, plan });
```

`authorize` selects create operations; no-ops need no write authority. Partial selection requires
`allowPartial: true`. `apply` validates the digest, provider, conflicts, and current provider state
before writing. A changed provider snapshot fails as stale instead of applying the old review.

Persist both complete receipts and the receipt carried by `PartialApplyError`.

## Build a cleanup plan

Cleanup starts from the exact source plan and apply receipt:

```ts
import { Deletion } from "domainkit";

const cleanupPlan =
  yield *
  Deletion.create({
    plan,
    receipt,
  });
```

`Deletion.create` re-reads each provider record by its receipt ID. It refuses to plan deletion when
the record is missing, drifted, belongs to another plan, or cannot be proven.

Present the cleanup operations as a new destructive review, then authorize and apply that digest:

```ts
const cleanupAuthorization = yield * Deletion.authorize(cleanupPlan);
const cleanupReceipt =
  yield *
  Deletion.apply({
    authorization: cleanupAuthorization,
    plan: cleanupPlan,
  });
```

Cleanup can also complete partially. Persist the partial deletion receipt and pass it as
`priorReceipt` on a retry so DomainKit resumes only the remaining authorized operations.

## Disconnect independently

Removing a domain grant and removing DNS records are separate decisions. A domain-level disconnect
normally preserves DNS. Offer cleanup only when a valid apply receipt exists and the user explicitly
reviews the deletion plan.

## Related documentation

- [Plans, authorization, and receipts](/docs/core/plans)
- [Integrate a host application](/docs/guides/host-integration)
- [Troubleshoot stale and partial operations](/docs/guides/troubleshooting)
