openapi: 3.1.0
info:
  title: MXCentral Public API
  version: v1
  description: >
    Public API contract for MXCentral v1. Authentication is local-auth only in v1.
    Endpoint and payload semantics are bounded by the canonical planning documents.
servers:
  - url: https://api.example.invalid
tags:
  - name: Status
  - name: Domains
  - name: Mailboxes
  - name: Quotas
  - name: Aliases
  - name: Relay
  - name: DKIM
  - name: Lists
  - name: Quarantine
  - name: SelfService
  - name: Backups
  - name: Migration
  - name: Audit
  - name: Support
  - name: Auth
  - name: PortalUsers
paths:
  /api/v1/status:
    get:
      tags: [Status]
      summary: Get consolidated runtime status
      operationId: status.show
      responses:
        '200':
          $ref: '#/components/responses/StatusOk'
  /api/v1/domains:
    get:
      tags: [Domains]
      summary: List hosted domains
      operationId: domains.index
      responses:
        '200':
          $ref: '#/components/responses/DomainCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Domains]
      summary: Create a hosted domain
      operationId: domains.store
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainCreateRequest'
      responses:
        '202':
          description: Domain creation accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Domain'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/domains/{domainId}:
    get:
      tags: [Domains]
      summary: Get one hosted domain
      operationId: domains.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DomainOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Domains]
      summary: Update one hosted domain
      operationId: domains.update
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainUpdateRequest'
      responses:
        '202':
          description: Domain update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Domain'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes:
    get:
      tags: [Mailboxes]
      summary: List mailboxes
      operationId: mailboxes.index
      responses:
        '200':
          $ref: '#/components/responses/MailboxCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Mailboxes]
      summary: Create a mailbox
      operationId: mailboxes.store
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxCreateRequest'
      responses:
        '202':
          description: Mailbox creation accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Mailbox'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/mailboxes/{mailboxId}:
    get:
      tags: [Mailboxes]
      summary: Get one mailbox
      operationId: mailboxes.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Mailboxes]
      summary: Update one mailbox
      operationId: mailboxes.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxUpdateRequest'
      responses:
        '202':
          description: Mailbox update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Mailbox'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/quota:
    get:
      tags: [Quotas]
      summary: Get mailbox quota settings
      operationId: mailboxes.quota.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxQuotaOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Quotas]
      summary: Replace mailbox quota settings
      operationId: mailboxes.quota.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxQuotaUpdateRequest'
      responses:
        '202':
          description: Mailbox quota update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxQuota'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/usage:
    get:
      tags: [Quotas]
      summary: Get mailbox usage information
      operationId: mailboxes.usage.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxUsageOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/settings:
    get:
      tags: [SelfService]
      summary: Get mailbox self-service settings
      operationId: mailboxes.settings.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxSettingsOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Update mailbox self-service settings
      operationId: mailboxes.settings.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxSettingsUpdateRequest'
      responses:
        '202':
          description: Mailbox settings update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxSettings'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/password:
    put:
      tags: [SelfService]
      summary: Change mailbox password
      operationId: mailboxes.password.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxPasswordUpdateRequest'
      responses:
        '202':
          description: Mailbox password change accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailboxPassword'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/forwards:
    get:
      tags: [SelfService]
      summary: Get mailbox forwarding rules
      operationId: mailboxes.forwards.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxForwardCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace mailbox forwarding rules
      operationId: mailboxes.forwards.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxForwardsUpdateRequest'
      responses:
        '202':
          description: Mailbox forwards update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxForwardCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/spam-policy:
    get:
      tags: [SelfService]
      summary: Get mailbox spam policy
      operationId: mailboxes.spam-policy.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxSpamPolicyOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace mailbox spam policy
      operationId: mailboxes.spam-policy.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxSpamPolicyUpdateRequest'
      responses:
        '202':
          description: Mailbox spam policy update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxSpamPolicy'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/allowlist:
    get:
      tags: [SelfService]
      summary: Get mailbox allowlist entries
      operationId: mailboxes.allowlist.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxFilterListOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace mailbox allowlist entries
      operationId: mailboxes.allowlist.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxFilterListUpdateRequest'
      responses:
        '202':
          description: Mailbox allowlist update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxFilterList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/blocklist:
    get:
      tags: [SelfService]
      summary: Get mailbox blocklist entries
      operationId: mailboxes.blocklist.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/MailboxFilterListOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace mailbox blocklist entries
      operationId: mailboxes.blocklist.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailboxFilterListUpdateRequest'
      responses:
        '202':
          description: Mailbox blocklist update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/MailboxFilterList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/aliases:
    get:
      tags: [Aliases]
      summary: List aliases
      operationId: aliases.index
      responses:
        '200':
          $ref: '#/components/responses/AliasCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Aliases]
      summary: Create an alias
      operationId: aliases.store
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AliasCreateRequest'
      responses:
        '202':
          description: Alias creation accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Alias'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/aliases/{aliasId}:
    get:
      tags: [Aliases]
      summary: Get one alias
      operationId: aliases.show
      parameters:
        - $ref: '#/components/parameters/AliasId'
      responses:
        '200':
          $ref: '#/components/responses/AliasOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Aliases]
      summary: Update one alias
      operationId: aliases.update
      parameters:
        - $ref: '#/components/parameters/AliasId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AliasUpdateRequest'
      responses:
        '202':
          description: Alias update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/Alias'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/vacation:
    get:
      tags: [SelfService]
      summary: Get vacation rule for one mailbox
      operationId: mailboxes.vacation.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/VacationOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace vacation rule for one mailbox
      operationId: mailboxes.vacation.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VacationRuleUpdateRequest'
      responses:
        '202':
          description: Vacation rule update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/VacationRule'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailboxes/{mailboxId}/sieve:
    get:
      tags: [SelfService]
      summary: Get active sieve script for one mailbox
      operationId: mailboxes.sieve.show
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      responses:
        '200':
          $ref: '#/components/responses/SieveOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [SelfService]
      summary: Replace active sieve script for one mailbox
      operationId: mailboxes.sieve.update
      parameters:
        - $ref: '#/components/parameters/MailboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SieveScriptUpdateRequest'
      responses:
        '202':
          description: Sieve script update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/SieveScript'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/relay:
    get:
      tags: [Relay]
      summary: List the global default outbound relay profile
      operationId: relay.index
      responses:
        '200':
          $ref: '#/components/responses/RelayCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    put:
      tags: [Relay]
      summary: Create or update the global default outbound relay profile
      operationId: relay.upsert
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GlobalRelayUpsertRequest'
      responses:
        '202':
          description: Global relay profile update accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RelayProfileAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/domains/{domainId}/relays:
    get:
      tags: [Relay]
      summary: List relay profiles for one domain and inherited global profile
      operationId: domains.relays.index
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/RelayCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [Relay]
      summary: Create or update a domain relay profile
      operationId: domains.relays.upsert
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainRelayUpsertRequest'
      responses:
        '202':
          description: Domain relay profile update accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RelayProfileAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/spam-policy:
    get:
      tags: [Domains]
      summary: Get domain spam policy defaults
      operationId: domains.spam-policy.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DomainSpamPolicyOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Domains]
      summary: Replace domain spam policy defaults
      operationId: domains.spam-policy.update
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainSpamPolicyUpdateRequest'
      responses:
        '202':
          description: Domain spam policy update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/DomainSpamPolicy'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/dns-helper:
    get:
      tags: [Domains]
      summary: Get DNS helper output for one domain
      operationId: domains.dns-helper.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DomainDnsHelperOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/allowlist:
    get:
      tags: [Domains]
      summary: Get domain allowlist entries
      operationId: domains.allowlist.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DomainFilterListOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Domains]
      summary: Replace domain allowlist entries
      operationId: domains.allowlist.update
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainFilterListUpdateRequest'
      responses:
        '202':
          description: Domain allowlist update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/DomainFilterList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/blocklist:
    get:
      tags: [Domains]
      summary: Get domain blocklist entries
      operationId: domains.blocklist.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DomainFilterListOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Domains]
      summary: Replace domain blocklist entries
      operationId: domains.blocklist.update
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainFilterListUpdateRequest'
      responses:
        '202':
          description: Domain blocklist update accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - $ref: '#/components/schemas/DomainFilterList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/dkim:
    get:
      tags: [DKIM]
      summary: Get DKIM selectors and DNS guidance for one domain
      operationId: domains.dkim.show
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/DkimOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [DKIM]
      summary: Rotate or generate DKIM selector for one domain
      operationId: domains.dkim.rotate
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DkimRotateRequest'
      responses:
        '202':
          description: DKIM rotation accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - type: object
                    properties:
                      audit_action:
                        type: string
                      dkim_key:
                        $ref: '#/components/schemas/DkimKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/domains/{domainId}/mailing-lists:
    get:
      tags: [Lists]
      summary: List mailing lists for one domain
      operationId: domains.mailing-lists.index
      parameters:
        - $ref: '#/components/parameters/DomainId'
      responses:
        '200':
          $ref: '#/components/responses/MailingListCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [Lists]
      summary: Create a mailing list in one domain
      operationId: domains.mailing-lists.create
      parameters:
        - $ref: '#/components/parameters/DomainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailingListCreateRequest'
      responses:
        '202':
          description: Mailing list creation accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailingListAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/mailing-lists/{listId}:
    patch:
      tags: [Lists]
      summary: Update one mailing list
      operationId: mailing-lists.update
      parameters:
        - $ref: '#/components/parameters/ListId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MailingListUpdateRequest'
      responses:
        '202':
          description: Mailing list update accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MailingListAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/quarantine/messages:
    get:
      tags: [Quarantine]
      summary: List quarantined messages with role and domain scoping
      operationId: quarantine.messages.index
      responses:
        '200':
          $ref: '#/components/responses/QuarantineCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/quarantine/messages/{messageId}/release:
    post:
      tags: [Quarantine]
      summary: Release one quarantined message
      operationId: quarantine.messages.release
      parameters:
        - $ref: '#/components/parameters/MessageId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QuarantineReleaseRequest'
      responses:
        '202':
          description: Quarantine release accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuarantineMessageRelease'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/backups:
    get:
      tags: [Backups]
      summary: List backup jobs and recent snapshots
      operationId: backups.index
      responses:
        '200':
          $ref: '#/components/responses/BackupCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/backups/run:
    post:
      tags: [Backups]
      summary: Trigger a local backup run
      operationId: backups.run
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BackupRunRequest'
      responses:
        '202':
          description: Backup job start accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackupJobAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/migrations:
    get:
      tags: [Migration]
      summary: List mailbox migration jobs
      operationId: migrations.index
      responses:
        '200':
          $ref: '#/components/responses/MigrationCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Migration]
      summary: Create a mailbox migration job
      operationId: migrations.create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MigrationCreateRequest'
      responses:
        '202':
          description: Migration creation accepted
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AcceptedEnvelope'
                  - type: object
                    properties:
                      audit_action:
                        type: string
                  - $ref: '#/components/schemas/MigrationJob'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/migrations/{migrationId}:
    get:
      tags: [Migration]
      summary: Get one mailbox migration job
      operationId: migrations.show
      parameters:
        - $ref: '#/components/parameters/MigrationId'
      responses:
        '200':
          $ref: '#/components/responses/MigrationOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/migrations/{migrationId}/events:
    get:
      tags: [Migration]
      summary: List detailed event history for one mailbox migration job
      operationId: migrations.events.index
      parameters:
        - $ref: '#/components/parameters/MigrationId'
      responses:
        '200':
          $ref: '#/components/responses/MigrationEventCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/audit-events:
    get:
      tags: [Audit]
      summary: List audit events
      operationId: audit-events.index
      responses:
        '200':
          $ref: '#/components/responses/AuditCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/support-bundles:
    get:
      tags: [Support]
      summary: List support bundles
      operationId: support-bundles.index
      responses:
        '200':
          $ref: '#/components/responses/SupportBundleCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Support]
      summary: Generate a support bundle
      operationId: support-bundles.create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SupportBundleCreateRequest'
      responses:
        '202':
          description: Support bundle generation accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SupportBundleAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/admin-users:
    get:
      tags: [PortalUsers]
      summary: List portal users
      operationId: portal-users.index
      responses:
        '200':
          $ref: '#/components/responses/PortalUserCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [PortalUsers]
      summary: Create a portal user
      operationId: portal-users.create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PortalUserCreateRequest'
      responses:
        '202':
          description: Portal user creation accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PortalUserAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/admin-users/{username}:
    patch:
      tags: [PortalUsers]
      summary: Update one portal user
      operationId: portal-users.update
      parameters:
        - $ref: '#/components/parameters/PortalUserId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PortalUserUpdateRequest'
      responses:
        '202':
          description: Portal user update accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PortalUserAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      tags: [PortalUsers]
      summary: Delete one portal user
      operationId: portal-users.delete
      parameters:
        - $ref: '#/components/parameters/PortalUserId'
      responses:
        '202':
          description: Portal user deletion accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PortalUserDeleted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/auth/state:
    get:
      tags: [Auth]
      summary: Get authenticated auth state and password posture
      operationId: auth.state.show
      responses:
        '200':
          $ref: '#/components/responses/AuthStateOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/recovery/request:
    post:
      tags: [Auth]
      summary: Create an emergency global admin recovery request artifact
      operationId: recovery.request
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecoveryRequest'
      responses:
        '202':
          description: Recovery request accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecoveryRequestAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/api-tokens:
    get:
      tags: [Auth]
      summary: List API tokens for the authenticated user
      operationId: api-tokens.index
      responses:
        '200':
          $ref: '#/components/responses/ApiTokenCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Auth]
      summary: Create an API token for the authenticated user
      operationId: api-tokens.create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiTokenCreateRequest'
      responses:
        '202':
          description: API token creation accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiTokenAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /api/v1/api-tokens/{tokenId}:
    delete:
      tags: [Auth]
      summary: Revoke one API token
      operationId: api-tokens.revoke
      parameters:
        - $ref: '#/components/parameters/TokenId'
      responses:
        '202':
          description: API token revoke accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiTokenRevoked'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/branding:
    get:
      tags: [Support]
      summary: List branding settings
      operationId: branding.index
      responses:
        '200':
          $ref: '#/components/responses/BrandingCollectionOk'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    put:
      tags: [Support]
      summary: Update global branding settings and SOGo logo override metadata
      operationId: branding.update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandingUpdateRequest'
      responses:
        '202':
          description: Branding update accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandingSettingAccepted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
components:
  parameters:
    DomainId:
      name: domainId
      in: path
      required: true
      schema:
        type: string
    MailboxId:
      name: mailboxId
      in: path
      required: true
      schema:
        type: string
    AliasId:
      name: aliasId
      in: path
      required: true
      schema:
        type: string
    MigrationId:
      name: migrationId
      in: path
      required: true
      schema:
        type: string
    ListId:
      name: listId
      in: path
      required: true
      schema:
        type: string
    MessageId:
      name: messageId
      in: path
      required: true
      schema:
        type: string
    PortalUserId:
      name: username
      in: path
      required: true
      schema:
        type: string
    TokenId:
      name: tokenId
      in: path
      required: true
      schema:
        type: string
  responses:
    Accepted:
      description: Request accepted for asynchronous processing.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AcceptedEnvelope'
    Unauthorized:
      description: Authentication is required or token or session is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Forbidden:
      description: Authenticated actor does not have scope for this resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: Resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    StatusOk:
      description: Consolidated runtime status payload.
      content:
        application/json:
          schema:
            $ref: './status-v1.yaml#/components/schemas/StatusPayload'
    DomainCollectionOk:
      description: Domain collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DomainCollection'
    DomainOk:
      description: Domain response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Domain'
    MailboxCollectionOk:
      description: Mailbox collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxCollection'
    MailboxOk:
      description: Mailbox response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Mailbox'
    MailboxQuotaOk:
      description: Mailbox quota response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxQuota'
    MailboxUsageOk:
      description: Mailbox usage response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxUsage'
    MailboxSettingsOk:
      description: Mailbox settings response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxSettings'
    MailboxForwardCollectionOk:
      description: Mailbox forwarding collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxForwardCollection'
    MailboxSpamPolicyOk:
      description: Mailbox spam policy response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxSpamPolicy'
    MailboxFilterListOk:
      description: Mailbox filter-list response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailboxFilterList'
    AliasCollectionOk:
      description: Alias collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AliasCollection'
    AliasOk:
      description: Alias response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Alias'
    VacationOk:
      description: Vacation rule response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/VacationRule'
    SieveOk:
      description: Sieve script response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SieveScript'
    RelayCollectionOk:
      description: Relay profile collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RelayProfileCollection'
    DomainSpamPolicyOk:
      description: Domain spam policy response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DomainSpamPolicy'
    DomainDnsHelperOk:
      description: Domain DNS helper response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DomainDnsHelper'
    DomainFilterListOk:
      description: Domain filter-list response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DomainFilterList'
    DkimOk:
      description: DKIM metadata response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DkimMetadata'
    MailingListCollectionOk:
      description: Mailing list collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MailingListCollection'
    QuarantineCollectionOk:
      description: Quarantine message collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/QuarantineMessageCollection'
    BackupCollectionOk:
      description: Backup collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/BackupCollection'
    MigrationCollectionOk:
      description: Migration collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MigrationCollection'
    MigrationOk:
      description: Migration response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MigrationJob'
    MigrationEventCollectionOk:
      description: Migration event collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/MigrationJobEventCollection'
    AuditCollectionOk:
      description: Audit event collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AuditCollection'
    SupportBundleCollectionOk:
      description: Support bundle collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SupportBundleCollection'
    AuthStateOk:
      description: Auth state response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AuthState'
    ApiTokenCollectionOk:
      description: API token collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiTokenCollection'
    PortalUserCollectionOk:
      description: Portal user collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PortalUserCollection'
    BrandingCollectionOk:
      description: Branding settings collection response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/BrandingCollection'
  schemas:
    DomainCreateRequest:
      type: object
      required: [name, is_active]
      properties:
        name:
          type: string
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
    DomainUpdateRequest:
      type: object
      required: [domain_id, name, is_active]
      properties:
        domain_id:
          type: string
        name:
          type: string
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
    MailboxCreateRequest:
      type: object
      required: [domain_id, local_part, owner_user_id, quota_mb, is_active]
      properties:
        domain_id:
          type: string
        local_part:
          type: string
        owner_user_id:
          type: string
        quota_mb:
          type: integer
          minimum: 0
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
    MailboxUpdateRequest:
      type: object
      required: [mailbox_id, owner_user_id, quota_mb, is_active]
      properties:
        mailbox_id:
          type: string
        owner_user_id:
          type: string
        quota_mb:
          type: integer
          minimum: 0
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
    MailboxQuotaUpdateRequest:
      type: object
      required: [mailbox_id, quota_mb]
      properties:
        mailbox_id:
          type: string
        quota_mb:
          type: integer
          minimum: 0
    MailboxSettingsUpdateRequest:
      type: object
      required: [mailbox_id, display_name, preferred_locale, reply_to_address, timezone]
      properties:
        mailbox_id:
          type: string
        display_name:
          type: string
          nullable: true
        preferred_locale:
          type: string
          nullable: true
        reply_to_address:
          type: string
          nullable: true
        timezone:
          type: string
          nullable: true
    MailboxPasswordUpdateRequest:
      type: object
      required: [mailbox_id, current_password, new_password]
      properties:
        mailbox_id:
          type: string
        current_password:
          type: string
        new_password:
          type: string
    MailboxForwardsUpdateRequest:
      type: object
      required: [mailbox_id, items]
      properties:
        mailbox_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MailboxForward'
    MailboxSpamPolicyUpdateRequest:
      type: object
      required: [mailbox_id, reject_threshold, tag_threshold, greylist_enabled, quarantine_enabled]
      properties:
        mailbox_id:
          type: string
        reject_threshold:
          type: number
        tag_threshold:
          type: number
        greylist_enabled:
          type: boolean
        quarantine_enabled:
          type: boolean
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
    MailboxFilterListUpdateRequest:
      type: object
      required: [mailbox_id, items]
      properties:
        mailbox_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MailboxFilterEntry'
    AliasCreateRequest:
      type: object
      required: [domain_id, source_address, destination_type, destinations, is_enabled]
      properties:
        domain_id:
          type: string
        source_address:
          type: string
        destination_type:
          type: string
          enum: [mailbox, external, mixed]
        destinations:
          type: array
          items:
            type: string
        is_enabled:
          type: boolean
    AliasUpdateRequest:
      type: object
      required: [alias_id, destination_type, destinations, is_enabled]
      properties:
        alias_id:
          type: string
        destination_type:
          type: string
          enum: [mailbox, external, mixed]
        destinations:
          type: array
          items:
            type: string
        is_enabled:
          type: boolean
    VacationRuleUpdateRequest:
      type: object
      required: [mailbox_id, is_enabled, subject, body_text, start_at, end_at, response_interval_hours]
      properties:
        mailbox_id:
          type: string
        is_enabled:
          type: boolean
        subject:
          type: string
        body_text:
          type: string
          nullable: true
        start_at:
          type: string
          format: date-time
          nullable: true
        end_at:
          type: string
          format: date-time
          nullable: true
        response_interval_hours:
          type: integer
          minimum: 0
    SieveScriptUpdateRequest:
      type: object
      required: [mailbox_id, name, script_body, is_active]
      properties:
        mailbox_id:
          type: string
        name:
          type: string
        script_body:
          type: string
          nullable: true
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
    RelayProfileUpsertRequest:
      type: object
      required: [host, port, username, password, encryption_mode, auth_enabled, is_enabled]
      properties:
        host:
          type: string
          nullable: true
        port:
          type: integer
          nullable: true
        username:
          type: string
          nullable: true
        password:
          type: string
          nullable: true
        encryption_mode:
          type: string
          nullable: true
        auth_enabled:
          type: boolean
        is_enabled:
          type: boolean
    GlobalRelayUpsertRequest:
      $ref: '#/components/schemas/RelayProfileUpsertRequest'
    DomainRelayUpsertRequest:
      allOf:
        - $ref: '#/components/schemas/RelayProfileUpsertRequest'
        - type: object
          required: [domain_id]
          properties:
            domain_id:
              type: string
    DomainSpamPolicyUpdateRequest:
      type: object
      required: [domain_id, reject_threshold, tag_threshold, greylist_enabled, quarantine_enabled]
      properties:
        domain_id:
          type: string
        reject_threshold:
          type: number
        tag_threshold:
          type: number
        greylist_enabled:
          type: boolean
        quarantine_enabled:
          type: boolean
    DomainFilterListUpdateRequest:
      type: object
      required: [domain_id, items]
      properties:
        domain_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/DomainFilterEntry'
    DkimRotateRequest:
      type: object
      required: [domain_id]
      properties:
        domain_id:
          type: string
        selector:
          type: string
    MailingListCreateRequest:
      type: object
      required: [domain_id, address, name, description, posting_policy, archive_enabled]
      properties:
        domain_id:
          type: string
        address:
          type: string
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        posting_policy:
          type: string
        archive_enabled:
          type: boolean
    MailingListUpdateRequest:
      type: object
      required: [list_id, name, description, posting_policy, archive_enabled]
      properties:
        list_id:
          type: string
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        posting_policy:
          type: string
        archive_enabled:
          type: boolean
    QuarantineReleaseRequest:
      type: object
      required: [message_id]
      properties:
        message_id:
          type: string
    BackupRunRequest:
      type: object
      required: [backup_type]
      properties:
        backup_type:
          type: string
          enum: [local, rsync]
    MigrationCreateRequest:
      type: object
      required: [domain_id, mailbox_id, source_host, source_port, source_username, source_password, source_encryption_mode]
      properties:
        domain_id:
          type: string
          nullable: true
        mailbox_id:
          type: string
          nullable: true
        source_host:
          type: string
        source_port:
          type: integer
        source_username:
          type: string
        source_password:
          type: string
        source_encryption_mode:
          type: string
    SupportBundleCreateRequest:
      type: object
      required: [include_logs, include_configs, include_service_status]
      properties:
        include_logs:
          type: boolean
        include_configs:
          type: boolean
        include_service_status:
          type: boolean
    RecoveryRequest:
      type: object
      required: [reason]
      properties:
        reason:
          type: string
    ApiTokenCreateRequest:
      type: object
      required: [name, abilities]
      properties:
        name:
          type: string
        abilities:
          type: array
          items:
            type: string
    BrandingUpdateRequest:
      type: object
      required: [sogo_logo_path, product_name_override, support_url, logo_asset_filename, logo_asset_media_type]
      properties:
        sogo_logo_path:
          type: string
          nullable: true
        product_name_override:
          type: string
          nullable: true
        support_url:
          type: string
          nullable: true
        logo_asset_filename:
          type: string
          nullable: true
        logo_asset_media_type:
          type: string
          nullable: true
    AcceptedEnvelope:
      type: object
      required: [status]
      properties:
        status:
          type: string
          const: accepted
        request_id:
          type: string
          nullable: true
        queued_at:
          type: string
          format: date-time
          nullable: true
    ErrorEnvelope:
      type: object
      required: [status, error]
      properties:
        status:
          type: string
          enum: [error, forbidden]
        error:
          type: string
        message:
          type: string
          nullable: true
        request_id:
          type: string
          nullable: true
    Domain:
      type: object
      required: [id, name, domain_status]
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        id:
          type: string
        name:
          type: string
        domain_status:
          type: string
          enum: [active, suspended, disabled, pending_dns]
        is_active:
          type: boolean
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
    DomainCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/Domain'
    Mailbox:
      type: object
      required: [id, address, domain_id, mailbox_status]
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        id:
          type: string
        address:
          type: string
        domain_id:
          type: string
        mailbox_status:
          type: string
          enum: [active, suspended, disabled, pending_migration]
        quota_mb:
          type: integer
          minimum: 0
        display_name:
          type: string
          nullable: true
        maildir_path:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
    MailboxCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/Mailbox'
    MailboxQuota:
      type: object
      required: [mailbox_id, quota_mb]
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        quota_mb:
          type: integer
        is_unlimited:
          type: boolean
          minimum: 0
        audit_action:
          type: string
          nullable: true
    MailboxUsage:
      type: object
      required: [mailbox_id, used_mb, quota_mb, percent_used, sampled_at]
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        used_mb:
          type: integer
          minimum: 0
        quota_mb:
          type: integer
          minimum: 0
        percent_used:
          type: integer
          minimum: 0
          maximum: 100
        sampled_at:
          type: string
          format: date-time
    MailboxSettings:
      type: object
      required: [mailbox_id]
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        display_name:
          type: string
          nullable: true
        preferred_locale:
          type: string
          nullable: true
        reply_to_address:
          type: string
          nullable: true
        timezone:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
    MailboxPassword:
      type: object
      required: [mailbox_id]
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        password_changed_at:
          type: string
          format: date-time
          nullable: true
        audit_action:
          type: string
          nullable: true
        mailbox:
          $ref: '#/components/schemas/Mailbox'
    MailboxForward:
      type: object
      properties:
        destination_address:
          type: string
        keep_local_copy:
          type: boolean
        is_enabled:
          type: boolean
    MailboxForwardCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MailboxForward'
        audit_action:
          type: string
          nullable: true
    MailboxSpamPolicy:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        reject_threshold:
          type: number
        tag_threshold:
          type: number
        greylist_enabled:
          type: boolean
        quarantine_enabled:
          type: boolean
        audit_action:
          type: string
          nullable: true
    MailboxFilterEntry:
      type: object
      properties:
        entry_type:
          type: string
          enum: [email, domain]
        value:
          type: string
    MailboxFilterList:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MailboxFilterEntry'
        audit_action:
          type: string
          nullable: true
    Alias:
      type: object
      required: [id, domain_id, source_address, destination_type, is_enabled]
      properties:
        status:
          type: string
        resource:
          type: string
        alias_id:
          type: string
        id:
          type: string
        domain_id:
          type: string
        source_address:
          type: string
        destination_type:
          type: string
          enum: [mailbox, external, mixed]
        is_enabled:
          type: boolean
        destinations:
          type: array
          items:
            type: string
        audit_action:
          type: string
          nullable: true
    AliasCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/Alias'
    VacationRule:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        is_enabled:
          type: boolean
        subject:
          type: string
        body_text:
          type: string
          nullable: true
        start_at:
          type: string
          format: date-time
          nullable: true
        end_at:
          type: string
          format: date-time
          nullable: true
        response_interval_hours:
          type: integer
          minimum: 0
        audit_action:
          type: string
          nullable: true
    SieveScript:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        mailbox_id:
          type: string
        name:
          type: string
        script_body:
          type: string
          nullable: true
        is_active:
          type: boolean
        audit_action:
          type: string
          nullable: true
        max_mailboxes:
          type: integer
        max_aliases:
          type: integer
        max_lists:
          type: integer
        default_quota_mb:
          type: integer
        created_by_user_id:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        created_at:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
    RelayProfile:
      type: object
      properties:
        id:
          type: string
        scope_type:
          type: string
          enum: [global, domain]
        scope_domain_id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        host:
          type: string
          nullable: true
        port:
          type: integer
          nullable: true
        username:
          type: string
          nullable: true
        encryption_mode:
          type: string
          nullable: true
        auth_enabled:
          type: boolean
        is_enabled:
          type: boolean
    RelayProfileCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        scope_type:
          type: string
        domain_id:
          type: string
          nullable: true
        items:
          type: array
          items:
            $ref: '#/components/schemas/RelayProfile'
    RelayProfileAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        scope_type:
          type: string
        domain_id:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        relay_profile:
          $ref: '#/components/schemas/RelayProfile'
    DomainSpamPolicy:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        reject_threshold:
          type: number
        tag_threshold:
          type: number
        greylist_enabled:
          type: boolean
        quarantine_enabled:
          type: boolean
        audit_action:
          type: string
          nullable: true
    DomainDnsHelper:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        mail_host:
          type: string
        autoconfig_host:
          type: string
        autodiscover_host:
          type: string
        records:
          type: array
          items:
            $ref: '#/components/schemas/DomainDnsRecord'
    DomainDnsRecord:
      type: object
      properties:
        type:
          type: string
        name:
          type: string
        value:
          type: string
    DomainFilterEntry:
      type: object
      properties:
        entry_type:
          type: string
          enum: [email, domain]
        value:
          type: string
    DomainFilterList:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/DomainFilterEntry'
        audit_action:
          type: string
          nullable: true
    DkimMetadata:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        selectors:
          type: array
          items:
            type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/DkimKey'
        audit_action:
          type: string
          nullable: true
        selector:
          type: string
          nullable: true
        public_txt_name:
          type: string
          nullable: true
        public_txt_value:
          type: string
          nullable: true
        last_rotated_at:
          type: string
          format: date-time
          nullable: true
        dkim_key:
          $ref: '#/components/schemas/DkimKey'
    DkimKey:
      type: object
      properties:
        id:
          type: string
        domain_id:
          type: string
        selector:
          type: string
        private_key_path:
          type: string
        public_txt_name:
          type: string
          nullable: true
        public_txt_value:
          type: string
          nullable: true
        status:
          type: string
        last_rotated_at:
          type: string
          format: date-time
          nullable: true
    MailingList:
      type: object
      properties:
        id:
          type: string
        domain_id:
          type: string
        address:
          type: string
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        posting_policy:
          type: string
        archive_enabled:
          type: boolean
    MailingListCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        domain_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MailingList'
    MailingListAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        list_id:
          type: string
        audit_action:
          type: string
          nullable: true
        domain_id:
          type: string
        address:
          type: string
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        posting_policy:
          type: string
        archive_enabled:
          type: boolean
    QuarantineMessage:
      type: object
      properties:
        id:
          type: string
        domain_id:
          type: string
        mailbox_id:
          type: string
          nullable: true
        status:
          type: string
        sender:
          type: string
          nullable: true
        recipient:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        size_bytes:
          type: integer
          nullable: true
        spam_score:
          type: number
          format: float
          nullable: true
        virus_detected:
          type: boolean
        quarantined_at:
          type: string
          format: date-time
          nullable: true
        released_at:
          type: string
          format: date-time
          nullable: true
    QuarantineMessageCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/QuarantineMessage'
    QuarantineMessageRelease:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        message_id:
          type: string
        audit_action:
          type: string
          nullable: true
        quarantine_message:
          $ref: '#/components/schemas/QuarantineMessage'
    BackupJob:
      type: object
      properties:
        id:
          type: string
        backup_type:
          type: string
          enum: [local, rsync]
        status:
          type: string
        artifact_path:
          type: string
          nullable: true
        size_bytes:
          type: integer
          nullable: true
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        last_error_text:
          type: string
          nullable: true
    BackupCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        available_actions:
          type: array
          items:
            type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/BackupJob'
    BackupJobAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        job_id:
          type: string
        audit_action:
          type: string
          nullable: true
        backup_job:
          $ref: '#/components/schemas/BackupJob'
    MigrationJob:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        job_id:
          type: string
        audit_action:
          type: string
          nullable: true
        id:
          type: string
        domain_id:
          type: string
          nullable: true
        mailbox_id:
          type: string
          nullable: true
        migration_status:
          type: string
        progress_current:
          type: integer
        progress_total:
          type: integer
        last_error_text:
          type: string
          nullable: true
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
    MigrationCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MigrationJob'
    MigrationJobEvent:
      type: object
      properties:
        id:
          type: string
        migration_job_id:
          type: string
        level:
          type: string
          enum: [info, warning, error]
        message:
          type: string
        event_meta_json:
          type: object
          nullable: true
        created_at:
          type: string
          format: date-time
    MigrationJobEventCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        migration_job_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/MigrationJobEvent'
    AuditEvent:
      type: object
      properties:
        id:
          type: string
        action:
          type: string
        actor_type:
          type: string
          enum: [user, system, installer, package_hook]
        actor_user_id:
          type: string
          nullable: true
        target_type:
          type: string
          nullable: true
        target_id:
          type: string
          nullable: true
        target_label:
          type: string
          nullable: true
        domain_id:
          type: string
          nullable: true
        request_id:
          type: string
          nullable: true
        ip_address:
          type: string
          nullable: true
        before_json:
          type: object
          nullable: true
        after_json:
          type: object
          nullable: true
        meta_json:
          type: object
          nullable: true
        created_at:
          type: string
          format: date-time
    AuditCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        available_actions:
          type: array
          items:
            type: string
        action_groups:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/AuditEvent'
    SupportBundle:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
        file_path:
          type: string
          nullable: true
        size_bytes:
          type: integer
          nullable: true
        redaction_profile:
          type: string
        created_by_user_id:
          type: string
          nullable: true
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        related_audit_actions:
          type: array
          items:
            type: string
    SupportBundleCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        available_actions:
          type: array
          items:
            type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/SupportBundle'
    SupportBundleAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        bundle_id:
          type: string
        audit_action:
          type: string
          nullable: true
        support_bundle:
          $ref: '#/components/schemas/SupportBundle'
    RecoveryRequestAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        audit_action:
          type: string
          nullable: true
        recovery:
          $ref: '#/components/schemas/RecoveryState'
    AuthState:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        user_id:
          type: string
        username:
          type: string
          nullable: true
        display_name:
          type: string
          nullable: true
        roles:
          type: array
          items:
            type: string
        portal_access:
          type: boolean
        recommended_path:
          type: string
        user_status:
          type: string
        must_change_password:
          type: boolean
        password_reset_reason:
          type: string
          nullable: true
        password_reset_requested_at:
          type: string
          format: date-time
          nullable: true
        local_auth_only:
          type: boolean
        last_login_at:
          type: string
          format: date-time
          nullable: true
        last_login_ip:
          type: string
          nullable: true
        related_audit_actions:
          type: array
          items:
            type: string
    RecoveryState:
      type: object
      properties:
        status:
          type: string
        requested_at:
          type: string
          format: date-time
        requested_by_uid:
          type: string
        username:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        reason:
          type: string
          nullable: true
        must_change_password:
          type: boolean
        request_file:
          type: string
        request_json_file:
          type: string
    ApiToken:
      type: object
      properties:
        id:
          type: string
        user_id:
          type: string
        name:
          type: string
        abilities:
          type: array
          items:
            type: string
        last_used_at:
          type: string
          format: date-time
          nullable: true
        token_preview:
          type: string
          nullable: true
    ApiTokenCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        user_id:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/ApiToken'
    ApiTokenAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        token_id:
          type: string
        token_preview:
          type: string
          nullable: true
        audit_action:
          type: string
          nullable: true
        api_token:
          $ref: '#/components/schemas/ApiToken'
    ApiTokenRevoked:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        token_id:
          type: string
        audit_action:
          type: string
          nullable: true
        revoked_token:
          $ref: '#/components/schemas/ApiToken'
    PortalUserCreateRequest:
      type: object
      required: [username, role]
      properties:
        username:
          type: string
        display_name:
          type: string
          nullable: true
        role:
          type: string
          enum:
            - global_admin
            - domain_admin
            - mailbox_user
        password:
          type: string
          nullable: true
        administered_domain_ids:
          type: array
          items:
            type: integer
        owned_mailbox_ids:
          type: array
          items:
            type: integer
    PortalUserUpdateRequest:
      type: object
      required: [role]
      properties:
        display_name:
          type: string
          nullable: true
        role:
          type: string
          enum:
            - global_admin
            - domain_admin
            - mailbox_user
        password:
          type: string
          nullable: true
        administered_domain_ids:
          type: array
          items:
            type: integer
        owned_mailbox_ids:
          type: array
          items:
            type: integer
    PortalUser:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        username:
          type: string
        display_name:
          type: string
        role:
          type: string
        user_id:
          type: integer
        password_hash:
          type: string
          nullable: true
        administered_domain_ids:
          type: array
          items:
            type: integer
        owned_mailbox_ids:
          type: array
          items:
            type: integer
    PortalUserCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/PortalUser'
    PortalUserAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        username:
          type: string
        portal_user:
          $ref: '#/components/schemas/PortalUser'
    PortalUserDeleted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        username:
          type: string
        action:
          type: string
    BrandingSetting:
      type: object
      properties:
        id:
          type: string
        scope_type:
          type: string
        scope_id:
          type: string
          nullable: true
        sogo_logo_path:
          type: string
          nullable: true
        product_name_override:
          type: string
          nullable: true
        support_url:
          type: string
          nullable: true
        logo_asset_filename:
          type: string
          nullable: true
        logo_asset_media_type:
          type: string
          nullable: true
        logo_asset_checksum:
          type: string
          nullable: true
        branding_sogo_dir:
          type: string
          nullable: true
        apply_strategy:
          type: string
          nullable: true
        last_applied_at:
          type: string
          format: date-time
          nullable: true
    BrandingSettingAccepted:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        scope_type:
          type: string
        scope_id:
          type: string
          nullable: true
        sogo_logo_path:
          type: string
          nullable: true
        product_name_override:
          type: string
          nullable: true
        support_url:
          type: string
          nullable: true
        logo_asset_filename:
          type: string
          nullable: true
        logo_asset_media_type:
          type: string
          nullable: true
        logo_asset_checksum:
          type: string
          nullable: true
        branding_sogo_dir:
          type: string
          nullable: true
        apply_strategy:
          type: string
          nullable: true
        last_applied_at:
          type: string
          format: date-time
          nullable: true
        audit_action:
          type: string
          nullable: true
        related_audit_actions:
          type: array
          items:
            type: string
    BrandingCollection:
      type: object
      properties:
        status:
          type: string
        resource:
          type: string
        available_actions:
          type: array
          items:
            type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/BrandingSetting'
