Skip to main content

Auto-configure sending-domain DNS on Cloudflare

Custom sending domains verify on SPF, DKIM, and DMARC records you publish in your DNS provider. When that DNS lives in a Cloudflare zone, Orbit can create the missing records for you and poke the email provider (Resend) to re-verify, all from the deliverability card on the domain row. For domains on other DNS providers, the same page has a provider-verify button that forces the provider to re-read the records you publish by hand.

1. How auto-configure works

The action reads a Cloudflare API token scoped to the domain’s zone, resolves the zone, pulls the exact record set the provider expects for your domain, creates whatever is missing, then tells the provider to re-check. It runs over the same record set the provider shows on the domain row, never the platform-default domain. The action is idempotent. Records already present in the zone (exact type, name, and content match) are left alone; only missing ones are created. You can rerun it after a partial failure and it picks up only what is still missing. Every record is created with Cloudflare proxying turned off (the orange-cloud toggle). A proxied TXT, MX, or CNAME record is invisible to mail infrastructure, so the DNS grid would show green while verification fails. Auto-configure prevents that specific trap. You can repeat as many times as you need. Cloudflare reports the final plan record by record: created, already present, or rejected with the provider’s error message.

What the flow does under the hood

  1. Resolve the zone. It walks up the domain’s labels (mail.acme.comacme.com) until Cloudflare returns an active zone your token can see.
  2. Read the required set. It asks the provider for the authoritative record list — the same records the provider’s own dashboard shows, so a stale provider cache cannot green-light a missing record.
  3. Diff against the zone. It plans an exact match on type, name, and content per record. TXT records at the same name with different content are additive in DNS, so a mismatch schedules a creation — it never overwrites one of your unrelated records.
  4. Publish and prove. It creates each gap with proxying off, pokes the provider to re-verify, stores the token only once it has shown it works, and reports the provider’s live state.

2. Set up the token

Create a Cloudflare API token with Zone → DNS → Edit on the zone that serves your sending domain (for mail.acme.com, scope the acme.com zone). Paste it into the auto-configure dialog on the domain row. Orbit stores it encrypted (settings.email.cloudflare_token) after the first successful run, so subsequent reconfigures do not need to paste anything.
Give the token the narrowest scope that works — one zone, DNS edit only — and rotate it if you regenerate the Cloudflare credentials. Orbit never logs the token and never returns it in any response.
If the stored token stops working, supply a new one on the next run and it replaces the stored value only after that run succeeds.

3. Run it from the dashboard

  1. Open Channels → Email, find your custom sending domain, and expand the deliverability card.
  2. Choose Cloudflare auto-configure.
  3. Paste the token on the first run; leave the field empty on later runs to reuse the stored one.
  4. Read the result per record. Anything marked already_present was already there; error carries the provider’s rejection so you can fix the qualifier (for example a name collision) and rerun.
The response also reports the provider state after the re-verify kick: sendable when the provider accepts the DNS, unverified while it is still re-checking, or unknown when the provider gave no definitive answer.

4. Provider verify — the manual path

If your DNS is managed outside Cloudflare (Registrar DNS, another DNS provider, or an internal resolver), publish the provider-required records by hand — the deliverability card shows them all — then choose Verify provider. The action forces the provider to re-read your DNS right now, without also re-running the live DNS resolve the full refresh does. Provider verify also proves a manual fix on a Cloudflare domain if you prefer to own the zone yourself. It is a no-op on the platform-default domain, which Orbit manages. The response tells you whether the provider accepted the kick and shows the freshly probed provider-verification card, so a refresh loop on the page picks up the live answer immediately. Cloudflare deletions and edits publish quickly, but public resolvers can cache the old answer. If provider verify still shows pending after a manual change, wait for the TTL you saw on the previous record and verify again.

5. Choose between the two

6. Security

  • Token scope. A Zone → DNS → Edit token on one zone. Nothing else.
  • Encrypted storage. The token sits next to your other provider credentials, encrypted at rest, and is written only after a run proves it.
  • No log exposure. The token is never logged and never returned.
  • Owner/admins only. The auto-configure route is gated to owner and admin roles, same as other write actions against the sending domain.

7. Troubleshooting

  • CLOUDFLARE_TOKEN_INVALID (400). The token was rejected. Recreate it with Zone → DNS → Edit on the right zone and retry.
  • CLOUDFLARE_ZONE_NOT_FOUND (404). No active Cloudflare zone matched any label of the domain. Check the zone status and the token’s zone scope.
  • CLOUDFLARE_TOKEN_REQUIRED (400). No token in the body and none stored. Paste one or run provider verify instead.
  • PROVIDER_DOMAIN_UNAVAILABLE (502). The provider has no record set for the domain yet. Re-add the sending domain under Channels → Email, then rerun.
  • A record marked error in the plan. The provider or Cloudflare rejected that specific record. The response carries the message; fix the named qualifier and rerun.
  • Mixed provider-matrix outcomes. Some matrix surfaces read the provider state from their own cached probe. They re-resolve on their next poll — the deliverability card will catch up within its normal cadence.
  • Record existed but with a trailing dot or case difference. Both the diff and lookup normalize for these, so they register as already_present rather than a duplicate creation.