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

# Add a domain to an organization

> Adds a domain as a pending claim and returns the TXT record to
publish at `_qumo-verification.<domain>`; then call verify. Several
organizations may hold a pending claim on one domain, but a domain
another organization has verified is refused with 409. Public mail
domains (gmail.com, outlook.com, yahoo.*, …) can never be claimed
(422), and IP addresses, single labels and special-use names
(localhost, .local, .internal, …) are refused with 400. Adding a
domain the organization holds already returns it unchanged (200).
An organization holds at most 20 domains (422). Needs the
organization's own plan to include SSO (Pay as you go or Enterprise;
402 on Free), and the organization not to be suspended (403).




## OpenAPI

````yaml /api-reference/openapi.yaml post /v1/organizations/{id}/domains
openapi: 3.1.0
info:
  title: Qumo Deploy REST API
  description: >
    The Qumo Deploy control plane: organizations, projects, signing keys,
    machine

    identities, access control, audit and billing. Relay credentials are not

    issued here: your app signs them with its own

    [signing keys](/guides/signing-keys).


    Every operation here is generated against the routes the server registers

    and checked in CI, so an operation in this reference exists and a route

    missing from it is deliberately internal (relay mesh, webhooks, probes,

    operator-only and browser sign-in routes).


    Errors are JSON objects with one `error` field. What each status code means

    is in [Error codes](/guides/error-codes); the token scope each permission

    needs is in [Scopes and permissions](/guides/scopes-permissions).
  version: 1.0.0
servers:
  - url: https://api.qumo-deploy.com
    description: Public API (Bearer tokens only; cookies are ignored)
security: []
tags:
  - name: Account
    description: The signed-in user's own sessions, tokens, security settings and data.
  - name: Device flow
    description: OAuth 2.0 device authorization, used by `qumod login`.
  - name: Organizations
    description: Organizations, their members and usage.
  - name: Projects
    description: Projects inside an organization.
  - name: Signing keys
    description: Keys your app signs relay credentials with.
  - name: Relays
    description: Where clients of a broadcast path connect.
  - name: Identities
    description: Machine identities, their tokens and IP allowlists.
  - name: Access control
    description: Roles, IAM bindings and invitations.
  - name: Audit
    description: The audit log and its webhook.
  - name: Notifications
    description: Notification policies.
  - name: Billing
    description: Plans, usage cost, budgets, invoices and payment methods.
  - name: Pricing
    description: Public price list.
paths:
  /v1/organizations/{id}/domains:
    parameters:
      - name: id
        in: path
        required: true
        description: The organization's id.
        schema:
          type: string
          format: uuid
        example: 3f8c2a1e-7b4d-4e9a-9c61-2d5e8f0a1b7c
    post:
      tags:
        - Organizations
      summary: Add a domain to an organization
      description: |
        Adds a domain as a pending claim and returns the TXT record to
        publish at `_qumo-verification.<domain>`; then call verify. Several
        organizations may hold a pending claim on one domain, but a domain
        another organization has verified is refused with 409. Public mail
        domains (gmail.com, outlook.com, yahoo.*, …) can never be claimed
        (422), and IP addresses, single labels and special-use names
        (localhost, .local, .internal, …) are refused with 400. Adding a
        domain the organization holds already returns it unchanged (200).
        An organization holds at most 20 domains (422). Needs the
        organization's own plan to include SSO (Pay as you go or Enterprise;
        402 on Free), and the organization not to be suspended (403).
      operationId: addOrganizationDomain
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrganizationDomainCreateRequest'
      responses:
        '200':
          description: The organization holds the domain already.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationDomain'
        '201':
          description: The domain was added, pending verification.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationDomain'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
      security:
        - token: []
        - cliSession: []
        - sessionCookie: []
          csrfToken: []
components:
  schemas:
    OrganizationDomainCreateRequest:
      type: object
      required:
        - domain
      properties:
        domain:
          type: string
          description: >-
            A fully qualified domain name. Unicode names are stored in their
            punycode form; a trailing dot is dropped.
          example: acme.example
    OrganizationDomain:
      type: object
      required:
        - id
        - organization_id
        - domain
        - status
        - verification
        - created_by
        - created_at
        - verified_at
        - last_checked_at
        - lapsed_at
        - grace_ends_at
        - frozen_at
      properties:
        id:
          type: string
          format: uuid
          example: 6e2a8c4f-1b3d-4a5e-9f7c-2d4b6a8e0c1f
        organization_id:
          type: string
          format: uuid
          example: 3f8c2a1e-7b4d-4e9a-9c61-2d5e8f0a1b7c
        domain:
          type: string
          description: The normalized name, lowercase ASCII with no trailing dot.
          example: acme.example
        status:
          type: string
          enum:
            - pending
            - verified
            - lapsed
            - frozen
          description: |
            `pending` until the record is found. `verified` once it is: the
            domain belongs to this organization alone. `lapsed` when a
            re-check found the record gone: everything keeps working until
            `grace_ends_at`, 14 days on, while Organization Admins are
            alerted. `frozen` after that: logins are not routed by the domain
            and no new users are provisioned under it, and no other
            organization can claim it. Verifying again restores a lapsed or
            frozen domain.
        verification:
          $ref: '#/components/schemas/OrganizationDomainVerification'
        created_by:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time
        verified_at:
          type: string
          format: date-time
          nullable: true
          description: The last time the record was found.
        last_checked_at:
          type: string
          format: date-time
          nullable: true
        last_check_error:
          type: string
          description: Why the last check did not find the record; absent when it did.
          example: TXT record not found
        lapsed_at:
          type: string
          format: date-time
          nullable: true
        grace_ends_at:
          type: string
          format: date-time
          nullable: true
          description: When a lapsed domain is frozen unless the record is back.
        frozen_at:
          type: string
          format: date-time
          nullable: true
    OrganizationDomainVerification:
      type: object
      description: >-
        The DNS record that proves control of the domain. Keep it published
        while the domain is claimed; every re-check looks for it.
      required:
        - record_type
        - record_name
        - record_value
      properties:
        record_type:
          type: string
          enum:
            - TXT
        record_name:
          type: string
          example: _qumo-verification.acme.example
        record_value:
          type: string
          example: qumo-verification=Zk3v9m8b0Q2xY7tH1pLwR4sN6cJ5aE8uD0iG3oK2fV1
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: A human-readable message.
          example: organization_id is required
  responses:
    BadRequest:
      description: >-
        Malformed request or failed validation. See [Error
        codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid credentials. See [Error codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: >-
        The organization's plan does not include this. See [Error
        codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >-
        Authenticated but not allowed (permission, token scope, IP allowlist or
        CSRF). See [Error codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: >-
        Not found, or not visible to the caller. See [Error
        codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: >-
        Conflicts with the current state. See [Error
        codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableEntity:
      description: >-
        Well-formed but semantically invalid. See [Error
        codes](/guides/error-codes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    token:
      type: http
      scheme: bearer
      bearerFormat: qumo_pat_
      description: >
        A personal access token (`POST /v1/auth/tokens`) or a machine

        identity token (`POST
        /v1/projects/{project_id}/identities/{identity_id}/tokens`),

        both prefixed `qumo_pat_`. A token's scopes cap what it can do; see

        [Scopes and permissions](/guides/scopes-permissions). A personal

        token names no organization: on a route whose path names no project or

        organization, pass `?organization_id=`.
    cliSession:
      type: http
      scheme: bearer
      description: |
        The session token `qumod login` obtains through the device flow
        (`POST /v1/auth/device/token`). It acts as the signed-in user, like a
        console session, and needs no CSRF token.
    sessionCookie:
      type: apiKey
      in: cookie
      name: __Host-qumo_session
      description: |
        The console's browser session. Accepted only on the console origin
        (`https://console.qumo-deploy.com`), never on `api.qumo-deploy.com`.
        A request that changes state must also send `X-CSRF-Token`.
    csrfToken:
      type: apiKey
      in: header
      name: X-CSRF-Token
      description: |
        CSRF token paired with `sessionCookie` on POST, PUT, PATCH and DELETE.
        Bearer requests do not send it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.