> ## Documentation Index
> Fetch the complete documentation index at: https://docs.puffle.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Sending Domain

> Register a new custom sending domain and auto-sync DKIM/SPF/DMARC records when possible.

**CLI:**

```bash theme={null}
puffle email domain create --domain <domain>
```

## Overview

Registers a new custom domain, auto-syncs the DKIM/SPF/DMARC records when Puffle can manage the DNS zone, triggers verification, and schedules the `domain-verify-poller` background task to converge status as DNS propagates.

Sending domains unlock creating email inboxes under that domain via `createEmailAccount`. Until a domain is `verified`, no inboxes can be provisioned on it.

<Note>
  This operation shares the URL path `/api/email/domains` with the list verb. See [List sending domains](/api-reference/senders/list-email-domains) to enumerate domains already registered.
</Note>

## AI agent notes

<Note>
  **Verification is a multi-step DNS dance.** Creating a domain does not mean it is usable. The flow is:

  1. `createEmailDomain` — Puffle receives DKIM/SPF/DMARC records; if managed DNS is available for the zone, records are written automatically and verification is triggered.
  2. If DNS auto-sync was skipped, `status` stays `pending` — the user must add the DNS records manually, then call `verifyEmailDomain` (no `?poll=true`) once.
  3. The `domain-verify-poller` background task re-checks the live domain status on an exponential-backoff schedule (10m, 20m, 40m, 80m, 160m, 320m) for up to 12 hours.

  **First-verifier-wins on ownership conflicts.** If another user's unverified claim exists on the same domain, creating here will drop the stale claim. If someone else has already `verified` the domain, you get a 409.
</Note>


## OpenAPI

````yaml post /api/email/domains
openapi: 3.0.3
info:
  title: Puffle API
  version: 1.0.0
  description: >-
    HTTP API for the Puffle GTM platform - Feed and Lead Finder search
    preparation, campaign-backed Outbound execution, Puffle Agent runs, sender
    management, and Unibox reply workflows. Designed for operation by both
    humans and autonomous AI agents. Agents should start at the [Agent
    Playbook](/guides/agent-playbook) which prescribes a workspace-context check
    (`GET /api/context`) and core user journeys with exact call ordering.
  contact:
    name: Puffle Support
    url: https://puffle.ai
servers:
  - url: https://app.puffle.ai
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Campaigns
    description: >-
      Campaign-backed Outbound execution. These endpoints manage draft,
      launching, active, paused, and completed Outbound runs with sequence
      nodes, contacts, and sender accounts.
  - name: Accounts
    description: Signed-in user account, settings, and workspace context endpoints.
  - name: Billing
    description: Billing customer portal and account billing endpoints.
  - name: Senders
    description: >-
      Connected sender accounts, email inboxes, sending domains, DNS
      verification, warmup, and sender capacity.
  - name: Socials
    description: >-
      Read connected LinkedIn and X accounts, review current and archived posts,
      and reconcile publication status. Creating, editing, scheduling,
      publishing, OAuth connection, analytics, and mentions remain
      dashboard-managed workflows and are not part of the public Bearer-token
      API.
paths:
  /api/email/domains:
    post:
      tags:
        - Senders
      summary: Create Sending Domain
      description: >-
        Register a new custom sending domain. Provisions the domain and fetches
        required DNS records, then attempts to write those records directly when
        managed DNS is available; if sync succeeds, verification is triggered
        immediately. Orphaned backing-domain resources from previous failed
        attempts are cleaned up and re-created. Duplicate claims by other users
        on an unverified domain are dropped (first-verifier-wins). Kicks off the
        `domain-verify-poller` background task so status converges
        asynchronously as DNS propagates.
      operationId: createEmailDomain
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                domain:
                  type: string
                  description: >-
                    Apex domain or subdomain to register. Lowercased
                    server-side. Must be a valid domain name.
              required:
                - domain
              additionalProperties: false
              description: >-
                Only `domain` is accepted. The handler provisions the domain,
                auto-syncs the DKIM/SPF/DMARC records when the zone is managed
                by Puffle, triggers verification if DNS was synced, and
                schedules the `domain-verify-poller` background task to converge
                status.
      responses:
        '201':
          description: >-
            Domain created. `status` is `verified` if DNS was already in place,
            `verifying` if the poller has been scheduled, or `pending` if DNS
            auto-sync failed and the user must add records manually.
          content:
            application/json:
              schema:
                type: object
                properties:
                  domain:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      user_id:
                        type: string
                        format: uuid
                      domain:
                        type: string
                        description: Apex or subdomain being registered, e.g. `acme.com`.
                      agentmail_domain_id:
                        description: >-
                          Backing provider identifier for the domain. Exposed
                          for legacy clients.
                        type: string
                        nullable: true
                      status:
                        type: string
                        enum:
                          - pending
                          - verifying
                          - verified
                          - failed
                        description: >-
                          Lifecycle status. `pending` — awaiting DNS records;
                          `verifying` — actively checking DNS; `verified` — all
                          records resolve and the domain can be used to create
                          inboxes; `failed` — the DNS records were rejected or
                          did not resolve (re-trigger via `/verify`).
                      dns_records:
                        description: >-
                          Provider-issued DNS records (DKIM/SPF/DMARC/CNAME).
                          Refreshed on every verify call so the UI always shows
                          live expected values.
                        type: array
                        items:
                          type: object
                          properties:
                            type:
                              type: string
                              description: DNS record type, e.g. `TXT`, `CNAME`, `MX`.
                            name:
                              type: string
                              description: Hostname the record sits at.
                            value:
                              type: string
                              description: Record value.
                            priority:
                              description: MX priority — present only for MX records.
                              type: number
                              nullable: true
                          required:
                            - type
                            - name
                            - value
                          additionalProperties: {}
                          description: >-
                            DNS record required to verify ownership/sending
                            rights. The DNS auto-sync step writes these
                            automatically when the zone is managed by Puffle;
                            otherwise the user must add them manually at their
                            DNS provider.
                        nullable: true
                      verified_at:
                        type: string
                        format: date-time
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      updated_at:
                        type: string
                        format: date-time
                        nullable: true
                    required:
                      - id
                      - user_id
                      - domain
                      - agentmail_domain_id
                      - status
                      - dns_records
                      - verified_at
                      - created_at
                      - updated_at
                    additionalProperties: false
                    description: >-
                      A custom sending domain. Domains are provisioned with
                      backing mail infrastructure, optionally auto-synced to
                      managed DNS, and then polled via the
                      `domain-verify-poller` background task until DNS
                      propagation is confirmed.
                required:
                  - domain
                additionalProperties: false
        '400':
          description: >-
            Validation failure or the backing verification service rejected the
            domain. Common causes: missing/malformed `domain`, or provider
            rejection after orphan cleanup was unable to recover.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                additionalProperties: false
        '401':
          description: Missing or invalid Bearer token.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                additionalProperties: false
        '409':
          description: >-
            Domain already registered. Either the caller owns it already, or
            another user has verified it first (first-verifier-wins).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                additionalProperties: false
        '500':
          description: >-
            Database write failed. If a backing domain was created before the DB
            insert failed, it is cleaned up to prevent orphans. DNS records are
            not rolled back because they are idempotent on re-registration.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                required:
                  - error
                additionalProperties: false
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: pk_live_...

````