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

# Add custom domain

> Add a custom domain to your account. The response includes the DNS records to publish (`records`). Once you publish them, the domain verifies automatically within a couple of minutes — poll `GET /v1/domains/{id}` until `status` is `verified`, then create inboxes on it. A pod-scoped API key creates the domain scoped to its own pod; inbox-scoped keys cannot manage domains.



## OpenAPI

````yaml https://api.openmail.sh/openapi.json post /v1/domains
openapi: 3.0.3
info:
  title: OpenMail API
  description: >-
    Email infrastructure API for AI agents. Create inboxes, send and receive
    email, and get real-time notifications via webhooks.
  version: 1.0.0
  license:
    name: Proprietary
servers:
  - url: https://api.openmail.sh
security:
  - bearerAuth: []
tags:
  - name: pods
    description: Isolated sub-accounts for your end users or tenants
  - name: inboxes
    description: Email inboxes and their webhook configuration
  - name: domains
    description: >-
      Custom domains: add your own sending/receiving domain, publish DNS, and
      verify it
  - name: messages
    description: Sending and listing messages
  - name: threads
    description: Conversation threads and read status
  - name: attachments
    description: Downloading and extracting text from attachments
  - name: policy
    description: >-
      Correspondent policy — provider-enforced allow/block rules and allowlist
      mode, scoped per account, pod, or inbox. Account-wide key only.
  - name: feedback
    description: Reporting bugs, friction, and feature requests to the OpenMail team
paths:
  /v1/domains:
    post:
      tags:
        - domains
      summary: Add custom domain
      description: >-
        Add a custom domain to your account. The response includes the DNS
        records to publish (`records`). Once you publish them, the domain
        verifies automatically within a couple of minutes — poll `GET
        /v1/domains/{id}` until `status` is `verified`, then create inboxes on
        it. A pod-scoped API key creates the domain scoped to its own pod;
        inbox-scoped keys cannot manage domains.
      operationId: createDomain
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - domain
              properties:
                domain:
                  type: string
                  minLength: 4
                  maxLength: 253
                  description: >-
                    The domain to add, e.g. `mail.acme.com`. A subdomain is
                    easiest to set up without affecting your existing mail.
                podId:
                  type: string
                  description: >-
                    Pod to scope the domain to. Omit or null for an account-wide
                    domain usable by any pod. A pod-scoped API key may only pass
                    its own pod.
                  nullable: true
      responses:
        '201':
          description: Domain added; publish the DNS records to verify it
          content:
            application/json:
              schema:
                description: Domain added; publish the DNS records to verify it
                allOf:
                  - $ref: '#/components/schemas/Domain'
        '202':
          description: >-
            Domain accepted but under review; DNS setup is blocked until
            approved. The response includes a `message` explaining why.
          content:
            application/json:
              schema:
                description: >-
                  Domain accepted but under review; DNS setup is blocked until
                  approved. The response includes a `message` explaining why.
                allOf:
                  - $ref: '#/components/schemas/Domain'
        '402':
          description: >-
            Pro plan: domain creation is paused because the account's monthly
            spend limit was reached (`spend_cap_paused`)
          content:
            application/json:
              schema:
                description: >-
                  Pro plan: domain creation is paused because the account's
                  monthly spend limit was reached (`spend_cap_paused`)
                allOf:
                  - $ref: '#/components/schemas/Error'
        '403':
          description: >-
            Plan does not allow custom domains (`plan_limit`), or the API key is
            scoped and cannot manage domains (`forbidden`)
          content:
            application/json:
              schema:
                description: >-
                  Plan does not allow custom domains (`plan_limit`), or the API
                  key is scoped and cannot manage domains (`forbidden`)
                allOf:
                  - $ref: '#/components/schemas/Error'
        '409':
          description: >-
            You already added this domain (`domain_exists`), it belongs to
            another account (`domain_taken`), or it needs manual reconnection
            (`identity_exists`)
          content:
            application/json:
              schema:
                description: >-
                  You already added this domain (`domain_exists`), it belongs to
                  another account (`domain_taken`), or it needs manual
                  reconnection (`identity_exists`)
                allOf:
                  - $ref: '#/components/schemas/Error'
        '422':
          description: >-
            Invalid domain (`invalid_domain`) or the domain was flagged as
            unsafe (`domain_blocked`)
          content:
            application/json:
              schema:
                description: >-
                  Invalid domain (`invalid_domain`) or the domain was flagged as
                  unsafe (`domain_blocked`)
                allOf:
                  - $ref: '#/components/schemas/Error'
        '502':
          description: The mail provider could not set up the domain (`provider_error`)
          content:
            application/json:
              schema:
                description: >-
                  The mail provider could not set up the domain
                  (`provider_error`)
                allOf:
                  - $ref: '#/components/schemas/Error'
components:
  schemas:
    Domain:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
          description: OpenMail domain ID
        domain:
          type: string
          description: >-
            The domain or subdomain, e.g. `example.com` or
            `agent-mail.example.com`.
        podId:
          type: string
          description: >-
            Pod this domain is scoped to, so only inboxes in that pod can use
            it, or null for an account-wide domain.
          nullable: true
        status:
          type: string
          enum:
            - pending
            - verifying
            - verified
            - failed
            - under_review
          description: >-
            `pending` — added, records not yet published. `verifying` — being
            checked. `verified` — sending and receiving enabled. `failed` —
            records missing or wrong; publishing them and calling verify again
            is the fix. `under_review` — flagged as suspicious; DNS setup is
            blocked until approved.
        records:
          type: array
          items:
            $ref: '#/components/schemas/DomainRecord'
          description: >-
            The DNS records to publish for this domain. Returned even where the
            values are the same for every customer, so sending infrastructure
            can change without breaking clients.
        verifiedAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    Error:
      type: object
      additionalProperties: true
      properties:
        error:
          type: string
        message:
          type: string
    DomainRecord:
      type: object
      additionalProperties: true
      description: >-
        A single DNS record to publish for a custom domain. Publish every record
        whose `status` is not already `valid`, then the domain verifies
        automatically within a couple of minutes.
      properties:
        type:
          type: string
          enum:
            - TXT
            - CNAME
            - MX
        name:
          type: string
          description: >-
            The record's host, fully qualified, e.g.
            `_dmarc.agent-mail.example.com`.
        value:
          type: string
          description: The record value to publish.
        priority:
          type: integer
          description: MX records only.
          nullable: true
        purpose:
          type: string
          enum:
            - spf
            - dkim
            - dmarc
            - mx
            - return-path
          description: >-
            What the record is for. Act on this rather than inferring from
            record shape: SPF must be merged into any the owner already has
            rather than replacing it, a DMARC policy at an apex can override the
            owner's own, and an apex MX takes over all their inbound mail.
        status:
          type: string
          enum:
            - missing
            - invalid
            - valid
          description: >-
            Whether this individual record is published and correct as of the
            last check. `missing` — not found yet. `invalid` — found but wrong.
            `valid` — published and correct. Shows which records are still
            outstanding, not only whether the whole domain passed.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key

````