Platform email
You need an administrator session for these commands. See Platform Management for choosing a platform, previews and confirmation.
On this pageManage platform-wide email settings · Check email status · Review sending limits
These commands decide who the shared sender may reach, who registers email domains and each project's outbound caps:
Requires: platform admin login
These commands need an admin session from void platform auth login. Automation uses VOID_OPERATOR_TOKEN from a secret store. Never put it in project files.
Now you will run void platform email policy and void platform email policy-set.
void platform email policy
void platform email policy-set <verified|domains|any> [--domains <domain[,domain...]>]Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email settings and void platform email settings-set.
void platform email settings
void platform email settings-set --domains <self-serve|admin>Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email limit and void platform email logs.
void platform email limit <project-id|slug> [--monthly <n>] [--burst <n>]
void platform email logs <project-id|slug> [--page <n>] [--limit <n>] [--json]Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email attempts and void platform email attempt-resolve.
void platform email attempts [--project <id|slug>] [--page <n>] [--limit <n>]
void platform email attempt-resolve <attempt-id> --ended --reason <text>Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email operation-resolve and void platform email shared-recipients.
void platform email operation-resolve <operation-id> --ended --outcome <applied|not-applied> --reason <text>
void platform email shared-recipients [--project <id|slug>] [--page <n>] [--limit <n>]Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email shared-recipient-resolve.
void platform email shared-recipient-resolve <recipient-id> --ended --outcome <applied|not-applied> --reason <text>Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
policy decides which recipients a project's <slug>+tag@<mail domain> sender reaches:
verified, the default: only that project's verified destinations.domains: also every address on the listed domains.any: no check.
No policy widens delivery to addresses on the platform's own mail domain. Those stay limited to verified destinations, so no project reaches another project's inbox without consent. Until the platform mail domain is onboarded for Email Sending, Cloudflare refuses unverified destinations. Under domains or any, those refusals show as UNVERIFIED_DESTINATION per recipient. Custom-domain sends are not affected.
settings-set --domains admin makes void email domain add print the admin's command instead of starting token setup. limit overrides a project's monthly and rolling 60-second caps. The defaults are 200 and 10. The project page in the admin UI shows and clears them.
logs shows kept receipt and recipient outcomes, including operation IDs, provider references, error codes and policy versions. Pages hold up to 100 records, newest first. Use --json for all fields. After a project is deleted, use its project ID to read metadata until the 30-day retention ends. Message content and credentials are never included.
attempts lists interrupted provider calls and the earliest time each can be resolved. Use the attempt ID from this list with attempt-resolve. Once the original Worker run has ended and the attempt is at least 24 hours old, attempt-resolve records outcome_unknown. It keeps the quota charge and releases the project and domain cleanup lock.
--ended confirms that the call is no longer active. --reason is stored in the operator audit log. Keep recipient addresses and message content out of the reason. The send is never retried. Use --plan to preview and --yes to apply without a prompt.
operation-resolve recovers a Cloudflare routing, Worker, secret, catch-all or Sending change whose outcome is unknown. Wait until the original run has ended and the operation is at least 24 hours old. Then inspect the exact resource the preview names. Confirm whether its write was applied or not-applied.
Applied writes continue at the next step. Not-applied writes retry the same saved intent. The running platform version must match that intent. Restore the matching version before you recover an operation from older code. Time alone never retries a write.
shared-recipients finds shared inbound addresses, provider rule names and IDs, and interrupted creation intents. This includes projects waiting for deletion.
If a creation outcome is unknown, use shared-recipient-resolve with the recipient ID from that list. Wait until the original run has ended and the intent is at least 24 hours old. Inspect the exact account, zone, recipient and rule that --plan names. Use Cloudflare's provider audit to decide whether creation was applied or not-applied. A missing rule alone does not prove not-applied. Put your finding in --reason. It is recorded in platform events.
Applied recovery needs the exact owned rule to exist and records its ID. Not-applied recovery releases the intent only after repeated provider checks and your confirmation. Either way, delivery stays not ready until normal reconciliation verifies it. Recovery never creates or deletes a provider rule and never reopens a deleting project. When you recover cleanup, retry the deletion afterward. Use --yes to apply the preview without a prompt. If the intent or provider ownership changes, inspect a new plan.
These commands register and maintain email domains for projects whose owners have no Cloudflare credential. Later steps use the domain you add in the first step.
Now you will run void platform email domains and void platform email domain-add.
void platform email domains [--project <id|slug>]
void platform email domain-add <domain> --project <id|slug> [--token-stdin]Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email domain-status and void platform email domain-sync.
void platform email domain-status <domain>
void platform email domain-sync <domain>Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
Next, run void platform email domain-rotate-secret and void platform email domain-remove.
void platform email domain-rotate-secret <domain>
void platform email domain-remove <domain> [--token-stdin]Example output: the requested record, list, or a confirmation. Commands with --plan show the change without applying it.
domain-add uses the platform's Cloudflare credential for zones in its account. For another account, pipe a scoped Cloudflare API token on stdin with --token-stdin --yes. Never pass the token as an argument. Managed custom inbound email needs the Cloudflare zone apex (example.com). If its MX records belong to another mail provider, use the project's shared address or another unused zone. Native Cloudflare deploys also support literal recipient addresses on subdomains. see Your own Cloudflare account.
domain-status shows inbound, outbound and credential readiness with the latest operation. A blocked operation resumes with domain-sync. A blocked rotation resumes with domain-rotate-secret, which keeps confirmed steps and the staged credential. An uncertain operation stays stopped until a read-back proves the result or an admin uses operation-resolve. Domains an admin adds show managed_by: admin. Their owners can list and inspect them, but sync, domain-rotate-secret and remove need these admin commands.
If cleanup after a project deletion is blocked by an expired or revoked Cloudflare token, run domain-remove <domain> --token-stdin --yes. Pipe in a new token scoped to the same account and zone. Cleanup resumes only if no other project uses the connection. For a live project, renew its token with domain-add instead.