openapi: 3.0.3
info:
  title: Mail.cx API
  version: "1.0.0"
  description: Create email addresses and receive emails via REST API.

servers:
  - url: https://api.mail.cx

tags:
  - name: Auth
    description: Create and sign in to email addresses
  - name: Mailbox
    description: Mailbox management and info
  - name: Messages
    description: Email message operations
  - name: Pro Accounts
    description: Pro mailbox management
  - name: Pro Domains
    description: Pro custom domain management
  - name: Pro Webhooks
    description: Pro webhook management
  - name: Pro Tokens
    description: Pro API token management

paths:
  # ──────────────────────────────────────────────
  # Mailbox API (free)
  # ──────────────────────────────────────────────

  /api/domains:
    get:
      tags: [Mailbox]
      summary: List available domains
      operationId: listDomains
      responses:
        "200":
          description: List of available domains
          content:
            application/json:
              schema:
                type: object
                properties:
                  domains:
                    type: array
                    items:
                      $ref: "#/components/schemas/Domain"

  /api/accounts:
    post:
      tags: [Auth]
      summary: Get a new email address
      operationId: createAccount
      description: |
        Create a new mailbox on any available system domain.

        **Pro users:** You can also call this endpoint with a Pro Bearer token
        (`Authorization: Bearer <pro_token>`) to create mailboxes on both system
        domains and your verified custom domains.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [address, password]
              properties:
                address:
                  type: string
                  format: email
                  example: user@domain.com
                password:
                  type: string
                  description: Mailbox password
                  example: mypassword123
      responses:
        "201":
          description: Email address created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  address:
                    type: string
                    format: email
                  token:
                    type: string
                    description: JWT token for accessing this mailbox
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Mailbox limit reached
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Address already taken
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/token:
    post:
      tags: [Auth]
      summary: Sign in to an existing mailbox
      operationId: login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [address, password]
              properties:
                address:
                  type: string
                  format: email
                password:
                  type: string
                  description: Mailbox password
      responses:
        "200":
          description: Sign-in successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  address:
                    type: string
                    format: email
                  token:
                    type: string
        "401":
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/me:
    get:
      tags: [Mailbox]
      summary: Get current mailbox info
      operationId: getMe
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Mailbox details
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  address:
                    type: string
                    format: email
                  role:
                    type: string
                    example: user
                  quota:
                    type: integer
                    example: 41943040
                  used:
                    type: integer
                    example: 12345
                  created_at:
                    type: string
                    format: date-time

  /api/account:
    delete:
      tags: [Mailbox]
      summary: Delete current email address
      operationId: deleteAccount
      security:
        - BearerAuth: []
      responses:
        "204":
          description: Email address and all messages deleted

  /api/messages:
    get:
      tags: [Messages]
      summary: List messages
      operationId: listMessages
      security:
        - BearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
            minimum: 1
      responses:
        "200":
          description: Paginated message list
          content:
            application/json:
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    items:
                      $ref: "#/components/schemas/EmailSummary"
                  page:
                    type: integer

  /api/messages/{id}:
    get:
      tags: [Messages]
      summary: Get a single message
      operationId: getMessage
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Full message detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailDetail"
        "404":
          description: Message not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Message expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      tags: [Messages]
      summary: Delete a message
      operationId: deleteMessage
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Message deleted
        "404":
          description: Message not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/messages/{id}/source:
    get:
      tags: [Messages]
      summary: Download raw EML source
      operationId: getMessageSource
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Raw email source
          content:
            message/rfc822:
              schema:
                type: string
                format: binary
        "404":
          description: Message not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Message expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/messages/{id}/attachments/{index}:
    get:
      tags: [Messages]
      summary: Download an attachment
      operationId: getAttachment
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: index
          in: path
          required: true
          schema:
            type: integer
            minimum: 0
      responses:
        "200":
          description: Attachment binary
          headers:
            Content-Disposition:
              schema:
                type: string
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid index
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Message expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/ws:
    get:
      tags: [Messages]
      summary: WebSocket for real-time email notifications
      operationId: websocket
      description: |
        Upgrade to WebSocket. After connecting, send `{"type": "auth", "token": "jwt"}`
        to authenticate. Server responds with `{"type": "auth_ok"}` or `{"error": "..."}`.
        On new mail the server pushes `{"id", "from", "subject", "account_id", "address"}`.
      responses:
        "101":
          description: Switching protocols (WebSocket upgrade)

  # ──────────────────────────────────────────────
  # Pro User API
  # ──────────────────────────────────────────────

  /pro/api/accounts:
    get:
      tags: [Pro Accounts]
      summary: List mailboxes under Pro account
      operationId: proListAccounts
      security:
        - ProBearerAuth: []
      responses:
        "200":
          description: List of mailboxes
          content:
            application/json:
              schema:
                type: object
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: "#/components/schemas/Account"
    post:
      tags: [Pro Accounts]
      summary: Create a mailbox under Pro account
      operationId: proCreateAccount
      security:
        - ProBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [address, password]
              properties:
                address:
                  type: string
                  format: email
                password:
                  type: string
                  description: Mailbox password
      responses:
        "201":
          description: Mailbox created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  address:
                    type: string
                    format: email
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Mailbox limit reached
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Address already taken
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /pro/api/accounts/{id}:
    delete:
      tags: [Pro Accounts]
      summary: Delete a mailbox
      operationId: proDeleteAccount
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Mailbox and all its emails deleted

  /pro/api/accounts/{id}/reset-password:
    put:
      tags: [Pro Accounts]
      summary: Reset a mailbox password
      operationId: proResetAccountPassword
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [password]
              properties:
                password:
                  type: string
                  description: New mailbox password (Argon2id-derived auth key)
      responses:
        "200":
          description: Password reset
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: password_reset
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Mailbox not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /pro/api/accounts/{id}/messages:
    get:
      tags: [Pro Accounts]
      summary: List messages for a mailbox
      operationId: proListAccountMessages
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: page
          in: query
          schema:
            type: integer
            default: 1
            minimum: 1
      responses:
        "200":
          description: Paginated message list
          content:
            application/json:
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    items:
                      $ref: "#/components/schemas/EmailSummary"
                  page:
                    type: integer

  /pro/api/domains:
    get:
      tags: [Pro Domains]
      summary: List custom domains
      operationId: proListDomains
      security:
        - ProBearerAuth: []
      responses:
        "200":
          description: List of custom domains
          content:
            application/json:
              schema:
                type: object
                properties:
                  domains:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        domain:
                          type: string
                        verify_status:
                          type: string
                          enum: [pending, verified]
                        verify_token:
                          type: string
                        verified_at:
                          type: string
                          format: date-time
                          nullable: true
                        mx_configured:
                          type: boolean
                        created_at:
                          type: string
                          format: date-time
    post:
      tags: [Pro Domains]
      summary: Add a custom domain
      operationId: proCreateDomain
      security:
        - ProBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                  example: custom.com
      responses:
        "201":
          description: Domain added with DNS instructions
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  domain:
                    type: string
                  verify_token:
                    type: string
                  dns_records:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                          example: TXT
                        host:
                          type: string
                        value:
                          type: string
                        priority:
                          type: integer
                          nullable: true
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Domain limit reached
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Domain already registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /pro/api/domains/{id}/verify:
    post:
      tags: [Pro Domains]
      summary: Verify domain DNS configuration
      operationId: proVerifyDomain
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  verify_status:
                    type: string
                    enum: [verified, pending]
                  txt_record:
                    type: boolean
                  mx_record:
                    type: boolean
                  dns_records:
                    type: array
                    description: Present when status is pending
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                        host:
                          type: string
                        value:
                          type: string
                        priority:
                          type: integer
                          nullable: true
        "404":
          description: Domain not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Domain expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /pro/api/domains/{id}:
    delete:
      tags: [Pro Domains]
      summary: Delete a custom domain
      operationId: proDeleteDomain
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Domain deleted

  /pro/api/webhooks:
    post:
      tags: [Pro Webhooks]
      summary: Create a webhook
      operationId: proCreateWebhook
      security:
        - ProBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                  example: ["email.received"]
      responses:
        "201":
          description: Webhook created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Webhook"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Webhook limit reached
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    get:
      tags: [Pro Webhooks]
      summary: List webhooks
      operationId: proListWebhooks
      security:
        - ProBearerAuth: []
      responses:
        "200":
          description: List of webhooks
          content:
            application/json:
              schema:
                type: object
                properties:
                  webhooks:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        url:
                          type: string
                          format: uri
                        events:
                          type: array
                          items:
                            type: string
                        status:
                          type: string
                          example: active
                        failure_count:
                          type: integer
                        last_triggered_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time

  /pro/api/webhooks/{id}:
    delete:
      tags: [Pro Webhooks]
      summary: Delete a webhook
      operationId: proDeleteWebhook
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Webhook deleted

  /pro/api/webhooks/{id}/rotate:
    post:
      tags: [Pro Webhooks]
      summary: Rotate webhook secret
      operationId: proRotateWebhookSecret
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: New secret generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  secret:
                    type: string
                    example: whsec_new...

  /pro/api/webhooks/{id}/deliveries:
    get:
      tags: [Pro Webhooks]
      summary: List webhook deliveries
      operationId: proListWebhookDeliveries
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: List of delivery attempts
          content:
            application/json:
              schema:
                type: object
                properties:
                  deliveries:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookDelivery"

  /pro/api/tokens:
    get:
      tags: [Pro Tokens]
      summary: List API tokens
      operationId: proListTokens
      security:
        - ProBearerAuth: []
      responses:
        "200":
          description: List of API tokens
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: "#/components/schemas/Token"
    post:
      tags: [Pro Tokens]
      summary: Create an API token
      operationId: proCreateToken
      security:
        - ProBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  example: My Token
      responses:
        "201":
          description: Token created (secret shown only once)
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  name:
                    type: string
                  token:
                    type: string
                    description: "Prefixed with tm_pro_. Only returned on creation."
                    example: tm_pro_...

  /pro/api/tokens/{id}:
    delete:
      tags: [Pro Tokens]
      summary: Revoke an API token
      operationId: proDeleteToken
      security:
        - ProBearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Token revoked

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token obtained from POST /api/accounts (create) or POST /api/token (sign in)
    ProBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token or API token (tm_pro_...) for pro endpoints

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
          description: Machine-readable error code
        message:
          type: string
          description: Human-readable error message

    EmailSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        sender:
          type: string
          format: email
        from:
          type: string
          description: "Display name and address, e.g. Name <a@b.com>"
        subject:
          type: string
        preview_text:
          type: string
        size:
          type: integer
        created_at:
          type: string
          format: date-time

    EmailDetail:
      type: object
      properties:
        id:
          type: string
          format: uuid
        sender:
          type: string
        from:
          type: string
        subject:
          type: string
        address:
          type: string
          format: email
        size:
          type: integer
        created_at:
          type: string
          format: date-time
        text_body:
          type: string
          nullable: true
        html_body:
          type: string
          nullable: true
        attachments:
          type: array
          items:
            $ref: "#/components/schemas/Attachment"

    Attachment:
      type: object
      properties:
        index:
          type: integer
        filename:
          type: string
        content_type:
          type: string
        size:
          type: integer

    Account:
      type: object
      properties:
        id:
          type: string
          format: uuid
        address:
          type: string
          format: email
        quota:
          type: integer
          example: 524288000
        used:
          type: integer
        created_at:
          type: string
          format: date-time

    Domain:
      type: object
      properties:
        id:
          type: string
          format: uuid
        domain:
          type: string
        default:
          type: boolean
        sort_order:
          type: integer

    Webhook:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        secret:
          type: string
          description: "Prefixed with whsec_"
        status:
          type: string
          example: active
        created_at:
          type: string
          format: date-time

    WebhookDelivery:
      type: object
      properties:
        id:
          type: string
          format: uuid
        event_type:
          type: string
          example: email.received
        event_id:
          type: string
        status_code:
          type: integer
        error:
          type: string
          nullable: true
        attempt:
          type: integer
        duration_ms:
          type: integer
        created_at:
          type: string
          format: date-time

    Token:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        last_used_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
          nullable: true
