openapi: 3.1.0
info:
  title: MIDDAG Account REST API
  version: 1.0.0
  description: >
    B2B customer lifecycle management REST API for the middag-account WordPress
    plugin.  Manages organizations, collaborators, entitlements, orders,
    invoices, quotes, licenses, contracts, environments, services, service
    requests, documents, downloads, sites, tax invoices, affiliates, Jira
    integration, and webhooks (Stripe / HubSpot).
  contact:
    name: MIDDAG
    url: https://www.middag.com.br
    email: michael@middag.com.br
  license:
    name: Proprietary

servers:
  - url: /wp-json/middag-account/v1
    description: WordPress REST API namespace

# ---------------------------------------------------------------------------
# Security schemes
# ---------------------------------------------------------------------------
security:
  - WordPressNonce: []
  - JWTBearer: []
  - WooCommerceKeys: []

components:
  securitySchemes:
    WordPressNonce:
      type: apiKey
      in: header
      name: X-WP-Nonce
      description: >
        WordPress cookie-based nonce.  Obtain via `wp_create_nonce('wp_rest')`.
    JWTBearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        RS256 JWT issued by `POST /auth/login` or `/auth/register`.
        Access token TTL 24 h, refresh token TTL 7 d.
        Claims: `sub` (user ID), `org` (organization ID), `roles`, `scopes`,
        `iss` ("middag-account"), `iat`, `exp`.
    WooCommerceKeys:
      type: apiKey
      in: query
      name: consumer_key
      description: >
        WooCommerce REST API consumer key + consumer_secret query params.

  # -------------------------------------------------------------------------
  # Common parameters
  # -------------------------------------------------------------------------
  parameters:
    PathId:
      name: id
      in: path
      required: true
      schema:
        type: integer
      description: Resource ID.
    PerPage:
      name: per_page
      in: query
      schema:
        type: integer
        default: 20
        minimum: 1
        maximum: 100
      description: Items per page.
    Page:
      name: page
      in: query
      schema:
        type: integer
        default: 1
        minimum: 1
      description: Page number.
    XMiddagOrganization:
      name: X-Middag-Organization
      in: header
      schema:
        type: integer
      description: >
        Organization context header.  Required for controllers with
        `requiresOrganization = true`.

  # -------------------------------------------------------------------------
  # Reusable response envelopes
  # -------------------------------------------------------------------------
  responses:
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Unauthorized:
      description: Missing or invalid authentication.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Forbidden:
      description: Authenticated but insufficient permissions.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ValidationError:
      description: Input validation failed (422).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Conflict:
      description: State conflict (409).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimit:
      description: Rate limit exceeded (429).
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'

  # -------------------------------------------------------------------------
  # Schemas
  # -------------------------------------------------------------------------
  schemas:
    # -- Envelope ----------------------------------------------------------
    SuccessEnvelope:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data: {}
        meta:
          type: object
          nullable: true
        message:
          type: string
          nullable: true
        errors:
          nullable: true
      required: [success, data]

    PaginatedEnvelope:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items: {}
        meta:
          $ref: '#/components/schemas/PaginationMeta'
        message:
          type: string
          nullable: true
        errors:
          nullable: true
      required: [success, data, meta]

    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        pages:
          type: integer
      required: [page, per_page, total, pages]

    ErrorEnvelope:
      type: object
      properties:
        success:
          type: boolean
          const: false
        data:
          nullable: true
        meta:
          nullable: true
        message:
          type: string
        errors:
          type: object
          properties:
            code:
              type: string
              description: >
                One of VALIDATION_ERROR, AUTHENTICATION_ERROR,
                AUTHORIZATION_ERROR, NOT_FOUND, CONFLICT, RATE_LIMIT,
                INTERNAL_ERROR, or a domain-specific code.
            fields:
              type: object
              additionalProperties:
                type: string
              description: Per-field validation errors (present on 422).
            detail:
              type: string
          required: [code]
      required: [success, message, errors]

    # -- Auth --------------------------------------------------------------
    User:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
          format: email
        name:
          type: string
        login:
          type: string
        avatar:
          type: string
          format: uri
          nullable: true
        phone:
          type: string
          nullable: true
        status:
          type: string
          enum: [ACTIVE, PENDING]
        active_team_id:
          type: integer
          nullable: true
        roles:
          type: array
          items:
            type: string
        registered:
          type: string
          format: date-time
      required: [id, email, name, login, roles]

    AuthTokens:
      type: object
      properties:
        access_token:
          type: string
        refresh_token:
          type: string
        token_type:
          type: string
          const: Bearer
        expires_in:
          type: integer
          description: Access token TTL in seconds.
      required: [access_token, refresh_token, token_type, expires_in]

    LoginRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
      required: [email, password]

    RegisterRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        name:
          type: string
      required: [email, password, name]

    RefreshRequest:
      type: object
      properties:
        refresh_token:
          type: string
      required: [refresh_token]

    UpdateProfileRequest:
      type: object
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
          format: password
        avatar:
          type: string
          format: uri
        phone:
          type: string
        provider:
          type: string
        active_team_id:
          type: integer

    ForgotPasswordRequest:
      type: object
      properties:
        email:
          type: string
          format: email
      required: [email]

    ResetPasswordRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        key:
          type: string
        password:
          type: string
          format: password
      required: [email, key, password]

    CheckUserRequest:
      type: object
      properties:
        email:
          type: string
          format: email
      required: [email]

    RegisterEmailOnlyRequest:
      type: object
      properties:
        email:
          type: string
          format: email
      required: [email]

    VerifyAccountRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        token:
          type: string
      required: [email, token]

    ResendVerificationRequest:
      type: object
      properties:
        email:
          type: string
          format: email
      required: [email]

    ValidateResetTokenRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        key:
          type: string
      required: [email, key]

    # -- Organization ------------------------------------------------------
    Organization:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        status:
          type: string
        owner_id:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        parent_id:
          type: integer
          nullable: true
        legal_name:
          type: string
          nullable: true
        cnpj:
          type: string
          nullable: true
        state_registration:
          type: string
          nullable: true
        email:
          type: string
          format: email
          nullable: true
        phone:
          type: string
          nullable: true
        address:
          type: string
          nullable: true
        address2:
          type: string
          nullable: true
        neighborhood:
          type: string
          nullable: true
        city:
          type: string
          nullable: true
        state:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        zipcode:
          type: string
          nullable: true
        ibge_code:
          type: string
          nullable: true
        type:
          type: string
          nullable: true
        verification_status:
          type: string
          nullable: true
        verified_at:
          type: string
          nullable: true
        stripe_customer_id_br:
          type: string
          nullable: true
        stripe_customer_id_global:
          type: string
          nullable: true
        hubspot_company_id_br:
          type: string
          nullable: true
        hubspot_company_id_global:
          type: string
          nullable: true
        id_number:
          type: string
          nullable: true
        requires_invoice_for_payment:
          type: boolean
        municipal_registration:
          type: string
          nullable: true
        tax_email:
          type: string
          nullable: true
        tax_param1:
          type: string
          nullable: true
        tax_param2:
          type: string
          nullable: true
        tax_param3:
          type: string
          nullable: true
        tax_param4:
          type: string
          nullable: true
        tax_param5:
          type: string
          nullable: true
        website:
          type: string
          nullable: true
        address_number:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        additional_info:
          type: string
          nullable: true
        contacts_support:
          type: string
          nullable: true
        contacts_payment:
          type: string
          nullable: true
        api_last_sync:
          type: string
          nullable: true
        api_data:
          type: string
          nullable: true
        verification_requires:
          type: boolean
      required: [id, name, status, owner_id, created_at, updated_at]

    CreateOrganizationRequest:
      type: object
      properties:
        org_name:
          type: string
        org_legalname:
          type: string
        org_documentnumber1:
          type: string
          description: CNPJ
        org_documentnumber2:
          type: string
          description: State registration
        org_email:
          type: string
          format: email
        org_phone:
          type: string
        org_address:
          type: string
        org_address2:
          type: string
        org_neighborhood:
          type: string
        org_city:
          type: string
        org_state:
          type: string
        org_country:
          type: string
        org_zipcode:
          type: string
        org_ibge_code:
          type: string
        org_type:
          type: string
      required: [org_name]

    UpdateOrganizationRequest:
      type: object
      properties:
        org_name:
          type: string
        org_legalname:
          type: string
        org_documentnumber1:
          type: string
        org_documentnumber2:
          type: string
        org_email:
          type: string
        org_phone:
          type: string
        org_address:
          type: string
        org_address2:
          type: string
        org_neighborhood:
          type: string
        org_city:
          type: string
        org_state:
          type: string
        org_country:
          type: string
        org_zipcode:
          type: string
        org_ibge_code:
          type: string
        org_type:
          type: string
        org_stripe_customer_id_br:
          type: string
        org_stripe_customer_id_global:
          type: string
        org_hubspot_company_id_br:
          type: string
        org_hubspot_company_id_global:
          type: string

    CnpjCheckRequest:
      type: object
      properties:
        cnpj:
          type: string
      required: [cnpj]

    CnpjCheckResponse:
      type: object
      properties:
        cnpj:
          type: string
        exists:
          type: boolean
        organization_id:
          type: integer
          nullable: true

    # -- Collaborator ------------------------------------------------------
    Collaborator:
      type: object
      properties:
        id:
          type: integer
        organization_id:
          type: integer
        parent_user_id:
          type: integer
          nullable: true
        user_id:
          type: integer
          nullable: true
        email:
          type: string
          format: email
        role:
          type: string
          enum: [owner, admin, member]
        status:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        started_at:
          type: string
          nullable: true
        expired_at:
          type: string
          nullable: true
        can_manage_org:
          type: boolean
        can_manage_finances:
          type: boolean
        can_manage_orders:
          type: boolean
        can_manage_licenses:
          type: boolean
        can_manage_tickets:
          type: boolean
        can_manage_quotes:
          type: boolean
        can_manage_contracts:
          type: boolean
        can_manage_documents:
          type: boolean
        can_manage_downloads:
          type: boolean
        invite_status:
          type: string
          nullable: true
          enum: [pending, accepted, rejected, expired, null]
      required: [id, organization_id, email, role, status]

    CreateCollaboratorRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        role:
          type: string
          enum: [admin, member]
        can_manage_org:
          type: boolean
        can_manage_finances:
          type: boolean
        can_manage_orders:
          type: boolean
        can_manage_licenses:
          type: boolean
        can_manage_tickets:
          type: boolean
        can_manage_quotes:
          type: boolean
        can_manage_contracts:
          type: boolean
        can_manage_documents:
          type: boolean
        can_manage_downloads:
          type: boolean
      required: [email]

    InviteCheckResponse:
      type: object
      properties:
        status:
          type: string
        is_valid:
          type: boolean
        organization_id:
          type: integer
        email:
          type: string
        expiration:
          type: string
          nullable: true
        expired:
          type: boolean

    InviteTokenRequest:
      type: object
      properties:
        token:
          type: string
      required: [token]

    # -- Entitlement -------------------------------------------------------
    Entitlement:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
          description: "Format: {CLASS}-{YYYYMM}{SEQ:4d}"
        class:
          type: string
          enum: [PLG, ENV, SVC, ORD, AFL, EDU]
        product_name:
          type: string
        status:
          type: string
          enum: [active, suspended, expired, cancelled]
        organization_id:
          type: integer
        company:
          type: string
          enum: [middag_br, middag_global]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        product_description:
          type: string
          nullable: true
        expires_at:
          type: string
          nullable: true
        quote_id:
          type: integer
          nullable: true
        auto_created:
          type: boolean
        metadata:
          type: string
          nullable: true
      required: [id, code, class, product_name, status, organization_id, company]

    CreateEntitlementRequest:
      type: object
      properties:
        class:
          type: string
          enum: [PLG, ENV, SVC, ORD, AFL, EDU]
        product_name:
          type: string
        organization_id:
          type: integer
        company:
          type: string
          enum: [middag_br, middag_global]
        product_description:
          type: string
        expires_at:
          type: string
        quote_id:
          type: integer
        metadata:
          type: string
      required: [class, product_name, organization_id, company]

    # -- Order -------------------------------------------------------------
    Order:
      type: object
      properties:
        id:
          type: integer
        number:
          type: string
        status:
          type: string
        total:
          type: number
          format: float
        currency:
          type: string
        date_created:
          type: string
          nullable: true
        date_modified:
          type: string
          nullable: true
        date_paid:
          type: string
          nullable: true
        payment_method:
          type: string
          nullable: true
        payment_method_title:
          type: string
          nullable: true
        customer_id:
          type: integer
          nullable: true
        billing_email:
          type: string
          nullable: true
        billing_name:
          type: string
          nullable: true
        organization_id:
          type: integer
          nullable: true
        company:
          type: string
          nullable: true
        items:
          type: array
          items:
            type: object
        notes:
          type: string
          nullable: true
      required: [id, number, status, total, currency]

    UpdateOrderStatusRequest:
      type: object
      properties:
        status:
          type: string
        note:
          type: string
      required: [status]

    # -- Invoice -----------------------------------------------------------
    Invoice:
      type: object
      properties:
        id:
          type: integer
        invoice_id:
          type: string
          nullable: true
        number:
          type: string
        status:
          type: string
        total:
          type: integer
          description: Amount in cents.
        currency:
          type: string
        issue_date:
          type: string
          format: date
        due_date:
          type: string
          format: date
        payment_method:
          type: string
          nullable: true
        payment_date:
          type: string
          format: date
          nullable: true
        link:
          type: string
          format: uri
          nullable: true
        tax_link:
          type: string
          format: uri
          nullable: true
        notes:
          type: string
          nullable: true
        order_id:
          type: integer
          nullable: true
        organization_id:
          type: integer
          nullable: true
        user_id:
          type: integer
          nullable: true
        origin:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required: [id, number, status, total, currency, issue_date, due_date]

    CreateInvoiceRequest:
      type: object
      properties:
        number:
          type: string
        status:
          type: string
        total:
          type: integer
        currency:
          type: string
        issue_date:
          type: string
        due_date:
          type: string
        payment_method:
          type: string
        link:
          type: string
        notes:
          type: string
        order_id:
          type: integer
        organization_id:
          type: integer
      required: [number]

    InvoicePdfResponse:
      type: object
      properties:
        url:
          type: string
          format: uri
        filename:
          type: string

    # -- Quote -------------------------------------------------------------
    Quote:
      type: object
      properties:
        id:
          type: integer
        quote_number:
          type: string
        status:
          type: string
          enum: [draft, sent, viewed, accepted, paid, fulfilled, rejected, expired, cancelled]
        organization_id:
          type: integer
        company:
          type: string
          enum: [middag_br, middag_global]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        hubspot_quote_id:
          type: string
          nullable: true
        hubspot_deal_id:
          type: string
          nullable: true
        line_items:
          type: string
          nullable: true
          description: JSON-encoded line items.
        subtotal:
          type: integer
          nullable: true
          description: Amount in cents.
        discount:
          type: integer
          nullable: true
          description: Discount in cents.
        total:
          type: integer
          nullable: true
          description: Total in cents.
        currency:
          type: string
          nullable: true
        valid_until:
          type: string
          format: date
          nullable: true
        accepted_at:
          type: string
          nullable: true
        rejected_at:
          type: string
          nullable: true
        paid_at:
          type: string
          nullable: true
        rejection_reason:
          type: string
          nullable: true
        payment_method:
          type: string
          nullable: true
        order_id:
          type: integer
          nullable: true
        entitlement_id:
          type: integer
          nullable: true
        contact_name:
          type: string
          nullable: true
        contact_email:
          type: string
          nullable: true
        metadata:
          type: string
          nullable: true
      required: [id, quote_number, status, organization_id, company]

    CreateQuoteRequest:
      type: object
      properties:
        quote_number:
          type: string
        organization_id:
          type: integer
        company:
          type: string
          enum: [middag_br, middag_global]
        line_items:
          type: string
        subtotal:
          type: integer
        discount:
          type: integer
        total:
          type: integer
        currency:
          type: string
        valid_until:
          type: string
        contact_name:
          type: string
        contact_email:
          type: string
        metadata:
          type: string
      required: [quote_number, organization_id, company]

    RejectQuoteRequest:
      type: object
      properties:
        reason:
          type: string

    # -- License -----------------------------------------------------------
    License:
      type: object
      properties:
        id:
          type: integer
          nullable: true
        license_key:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        product_id:
          type: integer
          nullable: true
        product_name:
          type: string
          nullable: true
        order_id:
          type: integer
          nullable: true
        activations_limit:
          type: integer
          nullable: true
        activations_current:
          type: integer
          nullable: true
        activated_domains:
          type: array
          items:
            type: string
        expiration_date:
          type: string
          nullable: true
        organization_id:
          type: integer
          nullable: true

    LicenseActivationRequest:
      type: object
      properties:
        domain:
          type: string
      required: [domain]

    # -- Contract ----------------------------------------------------------
    Contract:
      type: object
      properties:
        id:
          type: integer
        number:
          type: string
        year:
          type: string
        status:
          type: string
        user_id:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        organization_id:
          type: integer
          nullable: true
        order_id:
          type: integer
          nullable: true
        client_name:
          type: string
          nullable: true
        client_document_number:
          type: string
          nullable: true
        client_email:
          type: string
          nullable: true
        client_phone:
          type: string
          nullable: true
        client_address:
          type: string
          nullable: true
        client_legal_representative:
          type: string
          nullable: true
        client_legal_rep_doc_number:
          type: string
          nullable: true
        proposal:
          type: string
          nullable: true
        proposal_origin:
          type: string
          nullable: true
        object:
          type: string
          nullable: true
        deadline:
          type: string
          nullable: true
        duration:
          type: string
          nullable: true
        amount_monthly:
          type: integer
          nullable: true
          description: Amount in cents.
        amount_total:
          type: integer
          nullable: true
          description: Amount in cents.
        currency:
          type: string
          nullable: true
        payment_method:
          type: string
          nullable: true
        payment_due_date:
          type: string
          nullable: true
        service_limit:
          type: integer
          nullable: true
        service_amount:
          type: integer
          nullable: true
        project:
          type: string
          nullable: true
        info:
          type: string
          nullable: true
        link:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        issue_date:
          type: string
          nullable: true
        acceptance_date:
          type: string
          nullable: true
      required: [id, number, year, status, user_id]

    CreateContractRequest:
      type: object
      properties:
        number:
          type: string
        year:
          type: string
        organization_id:
          type: integer
        order_id:
          type: integer
        client_name:
          type: string
        client_document_number:
          type: string
        client_email:
          type: string
        object:
          type: string
        deadline:
          type: string
        duration:
          type: string
        amount_monthly:
          type: integer
        amount_total:
          type: integer
        currency:
          type: string
        payment_method:
          type: string
      required: [number, year]

    # -- Environment -------------------------------------------------------
    Environment:
      type: object
      properties:
        id:
          type: integer
        environment_name:
          type: string
        environment_type:
          type: string
          enum: [production, staging, development, sandbox]
        platform:
          type: string
          enum: [moodle, wordpress, custom]
        status:
          type: string
          enum: [provisioning, active, maintenance, suspended, decommissioned]
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        platform_version:
          type: string
          nullable: true
        url_primary:
          type: string
          nullable: true
        url_admin:
          type: string
          nullable: true
        url_staging:
          type: string
          nullable: true
        server_provider:
          type: string
          nullable: true
        server_region:
          type: string
          nullable: true
        server_ip:
          type: string
          nullable: true
        server_specs:
          type: string
          nullable: true
        backup_schedule:
          type: string
          nullable: true
        backup_retention_days:
          type: integer
          nullable: true
        last_backup_at:
          type: string
          nullable: true
        ssl_expires_at:
          type: string
          nullable: true
        dns_provider:
          type: string
          nullable: true
        contract_id:
          type: integer
          nullable: true
        notes:
          type: string
          nullable: true
        metadata:
          type: string
          nullable: true
      required: [id, environment_name, environment_type, platform, status, entitlement_id, organization_id]

    CreateEnvironmentRequest:
      type: object
      properties:
        environment_name:
          type: string
        environment_type:
          type: string
          enum: [production, staging, development, sandbox]
        platform:
          type: string
          enum: [moodle, wordpress, custom]
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        platform_version:
          type: string
        server_provider:
          type: string
        server_region:
          type: string
        notes:
          type: string
        metadata:
          type: string
      required: [environment_name, environment_type, platform, entitlement_id, organization_id]

    # -- Service -----------------------------------------------------------
    Service:
      type: object
      properties:
        id:
          type: integer
        service_type:
          type: string
          enum: [hosting, support, infrastructure, consulting, development, mobile-apps, migration, upgrade, project-management]
        title:
          type: string
        status:
          type: string
          enum: [proposal, approved, in_progress, on_hold, delivered, closed, cancelled]
        priority:
          type: string
          enum: [low, normal, high, urgent]
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        description:
          type: string
          nullable: true
        start_date:
          type: string
          nullable: true
        estimated_end_date:
          type: string
          nullable: true
        actual_end_date:
          type: string
          nullable: true
        contract_id:
          type: integer
          nullable: true
        assigned_team:
          type: string
          nullable: true
        metadata:
          type: string
          nullable: true
      required: [id, service_type, title, status, priority, entitlement_id, organization_id]

    CreateServiceBodyRequest:
      type: object
      properties:
        title:
          type: string
        service_type:
          type: string
          enum: [hosting, support, infrastructure, consulting, development, mobile-apps, migration, upgrade, project-management]
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        description:
          type: string
        priority:
          type: string
          enum: [low, normal, high, urgent]
        metadata:
          type: string
      required: [title, service_type, entitlement_id, organization_id]

    # -- ServiceRequest ----------------------------------------------------
    ServiceRequest:
      type: object
      properties:
        id:
          type: integer
        service_request_number:
          type: string
        title:
          type: string
        status:
          type: string
          enum: [open, in_progress, waiting, resolved, closed, cancelled]
        priority:
          type: string
          enum: [low, normal, high, urgent]
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        description:
          type: string
          nullable: true
        assigned_to:
          type: string
          nullable: true
        requested_by:
          type: integer
          nullable: true
        estimated_hours:
          type: string
          nullable: true
        actual_hours:
          type: string
          nullable: true
        due_date:
          type: string
          format: date
          nullable: true
        completed_at:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        client_notes:
          type: string
          nullable: true
        service_id:
          type: integer
          nullable: true
        metadata:
          type: string
          nullable: true
      required: [id, service_request_number, title, status, priority, entitlement_id, organization_id]

    CreateServiceRequestRequest:
      type: object
      properties:
        title:
          type: string
        entitlement_id:
          type: integer
        organization_id:
          type: integer
        description:
          type: string
        priority:
          type: string
          enum: [low, normal, high, urgent]
        service_id:
          type: integer
        metadata:
          type: string
      required: [title, entitlement_id, organization_id]

    # -- Document ----------------------------------------------------------
    Document:
      type: object
      properties:
        id:
          type: integer
        document_id:
          type: string
          nullable: true
        title:
          type: string
        type:
          type: string
          nullable: true
        format:
          type: string
          nullable: true
        link:
          type: string
          format: uri
          nullable: true
        description:
          type: string
          nullable: true
        category:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        date:
          type: string
          nullable: true
        order_id:
          type: integer
          nullable: true
        user_id:
          type: integer
          nullable: true
        author_id:
          type: integer
        status:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required: [id, title, status, author_id]

    CreateDocumentRequest:
      type: object
      properties:
        title:
          type: string
        type:
          type: string
        format:
          type: string
        link:
          type: string
        description:
          type: string
        category:
          type: string
        notes:
          type: string
      required: [title]

    # -- Download ----------------------------------------------------------
    Download:
      type: object
      properties:
        id:
          type: integer
          nullable: true
        product_name:
          type: string
          nullable: true
        version:
          type: string
          nullable: true
        file_name:
          type: string
          nullable: true
        file_size:
          type: integer
          nullable: true
        changelog:
          type: string
          nullable: true
        updated_at:
          type: string
          nullable: true
        requires_license:
          type: boolean

    DownloadFileResponse:
      type: object
      properties:
        file_url:
          type: string
          format: uri

    # -- TaxInvoice --------------------------------------------------------
    TaxInvoice:
      type: object
      properties:
        id:
          type: integer
        tax_invoice_id:
          type: string
          nullable: true
        number:
          type: string
        status:
          type: string
        total:
          type: string
        currency:
          type: string
        issue_date:
          type: string
          format: date
        link:
          type: string
          format: uri
          nullable: true
        notes:
          type: string
          nullable: true
        order_id:
          type: integer
          nullable: true
        organization_id:
          type: integer
          nullable: true
        subscription_id:
          type: integer
          nullable: true
        user_id:
          type: integer
          nullable: true
        origin:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required: [id, number, status, total, currency, issue_date]

    CreateTaxInvoiceRequest:
      type: object
      properties:
        number:
          type: string
        status:
          type: string
        total:
          type: string
        currency:
          type: string
        issue_date:
          type: string
        link:
          type: string
        notes:
          type: string
        order_id:
          type: integer
        organization_id:
          type: integer
      required: [number]

    # -- Site --------------------------------------------------------------
    Site:
      type: object
      properties:
        id:
          type: integer
        id_number:
          type: string
        user_id:
          type: integer
        order_id:
          type: integer
          nullable: true
        name:
          type: string
        plan_name:
          type: string
        plan_status:
          type: string
        url_alternative:
          type: string
          nullable: true
        url_custom:
          type: string
          nullable: true
        url_verified:
          type: boolean
        domain:
          type: string
          nullable: true
        started_at:
          type: string
          nullable: true
        stopped_at:
          type: string
          nullable: true
        finished_at:
          type: string
          nullable: true
        admin_first_name:
          type: string
          nullable: true
        admin_last_name:
          type: string
          nullable: true
        admin_email:
          type: string
          nullable: true
        admin_user_id:
          type: integer
          nullable: true
        is_installed:
          type: boolean
        is_configured:
          type: boolean
        is_active:
          type: boolean
        status:
          type: string
        created_at:
          type: string
          format: date-time
      required: [id, id_number, user_id, name, plan_name, plan_status, status]

    ConfigureSiteRequest:
      type: object
      properties:
        name:
          type: string
        url_alternative:
          type: string
        admin_first_name:
          type: string
        admin_last_name:
          type: string
        admin_email:
          type: string
          format: email
        admin_password:
          type: string
          format: password
          minLength: 4
      required: [name, url_alternative, admin_first_name, admin_last_name, admin_email, admin_password]

    SetDomainRequest:
      type: object
      properties:
        domain:
          type: string
      required: [domain]

    # -- Affiliate ---------------------------------------------------------
    AffiliateProfile:
      type: object
      description: Affiliate profile from SolidAffiliate adapter.
      additionalProperties: true

    CommissionSummary:
      type: object
      description: Commission totals grouped by status.
      additionalProperties: true

    # -- Customer (Stripe) -------------------------------------------------
    StripeCustomer:
      type: object
      description: Stripe Customer object (pass-through from Stripe API).
      additionalProperties: true

    FindUserByStripeResponse:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
        name:
          type: string

    # -- Jira --------------------------------------------------------------
    JiraProject:
      type: object
      properties:
        id:
          type: string
        key:
          type: string
        name:
          type: string
      additionalProperties: true

    JiraIssueType:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        subtask:
          type: boolean
      additionalProperties: true

    JiraIssue:
      type: object
      properties:
        id:
          type: string
        key:
          type: string
        summary:
          type: string
        status:
          type: string
        issue_type:
          type: string
      additionalProperties: true

    CreateJiraIssueRequest:
      type: object
      properties:
        project_key:
          type: string
        issue_type_id:
          type: string
        summary:
          type: string
        description:
          type: string
        extra_fields:
          type: object
      required: [project_key, issue_type_id, summary]

    JiraSearchRequest:
      type: object
      properties:
        jql:
          type: string
        fields:
          type: array
          items:
            type: string
        max_results:
          type: integer
          default: 50
      required: [jql]

    JiraTransition:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        to:
          type: string

    TransitionIssueRequest:
      type: object
      properties:
        transition_id:
          type: string
        fields:
          type: object
          nullable: true
      required: [transition_id]

    JiraComment:
      type: object
      properties:
        id:
          type: string
        body:
          type: string
        author:
          type: string
        created:
          type: string
      additionalProperties: true

    AddJiraCommentRequest:
      type: object
      properties:
        body:
          type: string
      required: [body]

    # -- Deleted -----------------------------------------------------------
    DeletedResponse:
      type: object
      properties:
        deleted:
          type: boolean
          const: true

# ===========================================================================
# Paths
# ===========================================================================
paths:
  # -------------------------------------------------------------------------
  # Auth
  # -------------------------------------------------------------------------
  /auth/login:
    post:
      tags: [Auth]
      summary: Authenticate with email and password
      description: Returns JWT access + refresh tokens.  Rate-limited.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
      responses:
        '200':
          description: Login successful.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - type: object
                            properties:
                              user:
                                $ref: '#/components/schemas/User'
                          - $ref: '#/components/schemas/AuthTokens'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/register:
    post:
      tags: [Auth]
      summary: Register a new user account
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterRequest'
      responses:
        '201':
          description: User created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - type: object
                            properties:
                              user:
                                $ref: '#/components/schemas/User'
                          - $ref: '#/components/schemas/AuthTokens'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/refresh:
    post:
      tags: [Auth]
      summary: Refresh access token
      description: Rotates the refresh token (replay detection).
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshRequest'
      responses:
        '200':
          description: Tokens refreshed.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - type: object
                            properties:
                              user:
                                $ref: '#/components/schemas/User'
                          - $ref: '#/components/schemas/AuthTokens'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/user:
    get:
      tags: [Auth]
      summary: Get current authenticated user
      responses:
        '200':
          description: User profile.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          user:
                            $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /auth/update-profile:
    post:
      tags: [Auth]
      summary: Update current user profile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProfileRequest'
      responses:
        '200':
          description: Profile updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          user:
                            $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'

  /auth/forgot-password:
    post:
      tags: [Auth]
      summary: Request password reset email
      description: Always returns success to prevent email enumeration.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ForgotPasswordRequest'
      responses:
        '200':
          description: Reset email sent (if account exists).
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/reset-password:
    post:
      tags: [Auth]
      summary: Reset password with token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResetPasswordRequest'
      responses:
        '200':
          description: Password reset.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/check-user:
    post:
      tags: [Auth]
      summary: Check if email is already registered
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckUserRequest'
      responses:
        '200':
          description: Email check result.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          exists:
                            type: boolean
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/login-sso:
    post:
      tags: [Auth]
      summary: Exchange WordPress session for JWT tokens
      security: []
      responses:
        '200':
          description: SSO login successful.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - type: object
                            properties:
                              user:
                                $ref: '#/components/schemas/User'
                          - $ref: '#/components/schemas/AuthTokens'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /auth/logout:
    post:
      tags: [Auth]
      summary: Logout and revoke all tokens
      responses:
        '200':
          description: Logout successful.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string

  /auth/register-email:
    post:
      tags: [Auth]
      summary: Register with email only (password set later)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterEmailOnlyRequest'
      responses:
        '201':
          description: User created, verification email sent.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - type: object
                            properties:
                              user:
                                $ref: '#/components/schemas/User'
                          - $ref: '#/components/schemas/AuthTokens'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/verify-account:
    post:
      tags: [Auth]
      summary: Verify account with email token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyAccountRequest'
      responses:
        '200':
          description: Account verified.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'

  /auth/resend-verification:
    post:
      tags: [Auth]
      summary: Resend account verification email
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResendVerificationRequest'
      responses:
        '200':
          description: Verification email sent.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimit'

  /auth/validate-reset-token:
    post:
      tags: [Auth]
      summary: Validate a password reset token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateResetTokenRequest'
      responses:
        '200':
          description: Token is valid.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          valid:
                            type: boolean
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Organizations
  # -------------------------------------------------------------------------
  /organizations:
    get:
      tags: [Organizations]
      summary: List organizations
      description: Non-admin users only see their own organizations.
      parameters:
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Organization'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Organizations]
      summary: Create an organization
      description: Creates the org and an owner collaborator for the current user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationRequest'
      responses:
        '201':
          description: Organization created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Organization'
        '422':
          $ref: '#/components/responses/ValidationError'

  /organizations/{id}:
    get:
      tags: [Organizations]
      summary: Get a single organization
      parameters:
        - $ref: '#/components/parameters/PathId'
      responses:
        '200':
          description: Organization details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Organization'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Organizations]
      summary: Update an organization
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrganizationRequest'
      responses:
        '200':
          description: Organization updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Organization'
        '404':
          $ref: '#/components/responses/NotFound'

  /organizations/cnpj-check:
    post:
      tags: [Organizations]
      summary: Check if a CNPJ is already registered
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CnpjCheckRequest'
      responses:
        '200':
          description: CNPJ check result.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/CnpjCheckResponse'

  # -------------------------------------------------------------------------
  # Collaborators
  # -------------------------------------------------------------------------
  /collaborators:
    get:
      tags: [Collaborators]
      summary: List collaborators for the current organization
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Collaborator'
    post:
      tags: [Collaborators]
      summary: Invite a collaborator
      description: Creates a collaborator record and sends an invitation email.
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCollaboratorRequest'
      responses:
        '201':
          description: Collaborator created with pending invite.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Collaborator'
        '422':
          $ref: '#/components/responses/ValidationError'

  /collaborators/{id}:
    get:
      tags: [Collaborators]
      summary: Get a single collaborator
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Collaborator details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Collaborator'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Collaborators]
      summary: Update a collaborator
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCollaboratorRequest'
      responses:
        '200':
          description: Collaborator updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Collaborator'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Collaborators]
      summary: Delete a collaborator
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Collaborator deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'
        '422':
          $ref: '#/components/responses/ValidationError'

  /collaborators/{id}/resend-invite:
    post:
      tags: [Collaborators]
      summary: Resend invitation email
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Invite resent.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
                          collaborator:
                            $ref: '#/components/schemas/Collaborator'
        '422':
          $ref: '#/components/responses/ValidationError'

  /collaborators/invite/check:
    get:
      tags: [Collaborators]
      summary: Check invite status by token
      security: []
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invite status.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/InviteCheckResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /collaborators/invite/accept:
    post:
      tags: [Collaborators]
      summary: Accept an invite (requires authenticated user)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InviteTokenRequest'
      responses:
        '200':
          description: Invite accepted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
                          collaborator:
                            $ref: '#/components/schemas/Collaborator'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'

  /collaborators/invite/reject:
    post:
      tags: [Collaborators]
      summary: Reject an invite (no auth required)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InviteTokenRequest'
      responses:
        '200':
          description: Invite rejected.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          message:
                            type: string
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Entitlements
  # -------------------------------------------------------------------------
  /entitlements:
    get:
      tags: [Entitlements]
      summary: List entitlements
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: class
          in: query
          schema:
            type: string
            enum: [PLG, ENV, SVC, ORD, AFL, EDU]
          description: Filter by entitlement class.
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Entitlement'
    post:
      tags: [Entitlements]
      summary: Create an entitlement
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEntitlementRequest'
      responses:
        '201':
          description: Entitlement created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Entitlement'
        '422':
          $ref: '#/components/responses/ValidationError'

  /entitlements/{id}:
    get:
      tags: [Entitlements]
      summary: Get an entitlement by ID
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Entitlement details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Entitlement'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Entitlements]
      summary: Update an entitlement
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEntitlementRequest'
      responses:
        '200':
          description: Entitlement updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Entitlement'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Entitlements]
      summary: Delete an entitlement
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Entitlement deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'
        '422':
          $ref: '#/components/responses/ValidationError'

  /entitlements/code/{code}:
    get:
      tags: [Entitlements]
      summary: Get an entitlement by code
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
            pattern: '^[A-Z]{3}-\d+$'
          description: "Entitlement code (e.g. PLG-2026010001)"
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Entitlement details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Entitlement'
        '404':
          $ref: '#/components/responses/NotFound'

  # -------------------------------------------------------------------------
  # Orders
  # -------------------------------------------------------------------------
  /orders:
    get:
      tags: [Orders]
      summary: List orders for the current organization
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Order'

  /orders/{id}:
    get:
      tags: [Orders]
      summary: Get a single order
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Order details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Order'
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/{id}/status:
    put:
      tags: [Orders]
      summary: Update order status (admin only)
      parameters:
        - $ref: '#/components/parameters/PathId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrderStatusRequest'
      responses:
        '200':
          description: Status updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          updated:
                            type: boolean
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'

  /orders/{id}/payment:
    get:
      tags: [Orders]
      summary: Get order payment info
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Payment information.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        additionalProperties: true
        '404':
          $ref: '#/components/responses/NotFound'

  # -------------------------------------------------------------------------
  # Invoices
  # -------------------------------------------------------------------------
  /invoices:
    get:
      tags: [Invoices]
      summary: List invoices
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Invoice'
    post:
      tags: [Invoices]
      summary: Create an invoice
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateInvoiceRequest'
      responses:
        '201':
          description: Invoice created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Invoice'
        '422':
          $ref: '#/components/responses/ValidationError'

  /invoices/{id}:
    get:
      tags: [Invoices]
      summary: Get a single invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Invoice details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Invoice'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Invoices]
      summary: Update an invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateInvoiceRequest'
      responses:
        '200':
          description: Invoice updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Invoice'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Invoices]
      summary: Delete an invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Invoice deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  /invoices/order/{order_id}:
    get:
      tags: [Invoices]
      summary: List invoices for an order
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: integer
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Invoices for this order.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          data:
                            type: array
                            items:
                              $ref: '#/components/schemas/Invoice'
                          total:
                            type: integer

  /invoices/organization/{organization_id}:
    get:
      tags: [Invoices]
      summary: List invoices for an organization
      parameters:
        - name: organization_id
          in: path
          required: true
          schema:
            type: integer
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Invoices for this organization.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          data:
                            type: array
                            items:
                              $ref: '#/components/schemas/Invoice'
                          total:
                            type: integer

  /invoices/last/order/{order_id}:
    get:
      tags: [Invoices]
      summary: Get the most recent invoice for an order
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: integer
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Last invoice.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Invoice'
        '404':
          $ref: '#/components/responses/NotFound'

  /invoices/{id}/pdf:
    get:
      tags: [Invoices]
      summary: Get invoice PDF download URL
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: PDF URL.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/InvoicePdfResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  # -------------------------------------------------------------------------
  # Quotes
  # -------------------------------------------------------------------------
  /quotes:
    get:
      tags: [Quotes]
      summary: List quotes
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: status
          in: query
          schema:
            type: string
            enum: [draft, sent, viewed, accepted, paid, fulfilled, rejected, expired, cancelled]
        - name: company
          in: query
          schema:
            type: string
            enum: [middag_br, middag_global]
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Quote'
    post:
      tags: [Quotes]
      summary: Create a quote
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQuoteRequest'
      responses:
        '201':
          description: Quote created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Quote'
        '422':
          $ref: '#/components/responses/ValidationError'

  /quotes/{id}:
    get:
      tags: [Quotes]
      summary: Get a single quote
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Quote details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Quote'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Quotes]
      summary: Update a quote
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQuoteRequest'
      responses:
        '200':
          description: Quote updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Quote'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Quotes]
      summary: Delete a quote
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Quote deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  /quotes/{id}/accept:
    post:
      tags: [Quotes]
      summary: Accept a quote
      description: Transitions quote to accepted and creates a WooCommerce order.
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Quote accepted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Quote'
        '422':
          $ref: '#/components/responses/ValidationError'

  /quotes/{id}/reject:
    post:
      tags: [Quotes]
      summary: Reject a quote
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RejectQuoteRequest'
      responses:
        '200':
          description: Quote rejected.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Quote'
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Licenses
  # -------------------------------------------------------------------------
  /licenses:
    get:
      tags: [Licenses]
      summary: List licenses
      description: Returns 503 if the license system is unavailable.
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/License'
        '503':
          description: License system unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /licenses/{id}:
    get:
      tags: [Licenses]
      summary: Get a single license
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: License details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/License'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          description: License system unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /licenses/{id}/activate:
    post:
      tags: [Licenses]
      summary: Activate a license on a domain
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LicenseActivationRequest'
      responses:
        '200':
          description: License activated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/License'
        '422':
          $ref: '#/components/responses/ValidationError'

  /licenses/{id}/deactivate:
    post:
      tags: [Licenses]
      summary: Deactivate a license on a domain
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LicenseActivationRequest'
      responses:
        '200':
          description: License deactivated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/License'
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Contracts
  # -------------------------------------------------------------------------
  /contracts:
    get:
      tags: [Contracts]
      summary: List contracts
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Contract'
    post:
      tags: [Contracts]
      summary: Create a contract
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractRequest'
      responses:
        '201':
          description: Contract created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Contract'
        '422':
          $ref: '#/components/responses/ValidationError'

  /contracts/{id}:
    get:
      tags: [Contracts]
      summary: Get a single contract
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Contract details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Contract'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Contracts]
      summary: Update a contract
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractRequest'
      responses:
        '200':
          description: Contract updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Contract'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Contracts]
      summary: Delete a contract
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Contract deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Environments
  # -------------------------------------------------------------------------
  /environments:
    get:
      tags: [Environments]
      summary: List environments
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: status
          in: query
          schema:
            type: string
            enum: [provisioning, active, maintenance, suspended, decommissioned]
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Environment'
    post:
      tags: [Environments]
      summary: Create an environment
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEnvironmentRequest'
      responses:
        '201':
          description: Environment created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Environment'
        '422':
          $ref: '#/components/responses/ValidationError'

  /environments/{id}:
    get:
      tags: [Environments]
      summary: Get an environment
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Environment details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Environment'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Environments]
      summary: Update an environment
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEnvironmentRequest'
      responses:
        '200':
          description: Environment updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Environment'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Environments]
      summary: Delete an environment
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Environment deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Services
  # -------------------------------------------------------------------------
  /services:
    get:
      tags: [Services]
      summary: List services
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: status
          in: query
          schema:
            type: string
            enum: [proposal, approved, in_progress, on_hold, delivered, closed, cancelled]
        - name: type
          in: query
          schema:
            type: string
            enum: [hosting, support, infrastructure, consulting, development, mobile-apps, migration, upgrade, project-management]
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Service'
    post:
      tags: [Services]
      summary: Create a service
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateServiceBodyRequest'
      responses:
        '201':
          description: Service created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Service'
        '422':
          $ref: '#/components/responses/ValidationError'

  /services/{id}:
    get:
      tags: [Services]
      summary: Get a single service
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Service details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Service'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Services]
      summary: Update a service
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateServiceBodyRequest'
      responses:
        '200':
          description: Service updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Service'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Services]
      summary: Delete a service
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Service deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Service Requests
  # -------------------------------------------------------------------------
  /service-requests:
    get:
      tags: [ServiceRequests]
      summary: List service requests
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
        - name: status
          in: query
          schema:
            type: string
            enum: [open, in_progress, waiting, resolved, closed, cancelled]
        - name: service_id
          in: query
          schema:
            type: integer
        - name: entitlement_id
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/ServiceRequest'
    post:
      tags: [ServiceRequests]
      summary: Create a service request
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateServiceRequestRequest'
      responses:
        '201':
          description: Service request created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/ServiceRequest'
        '422':
          $ref: '#/components/responses/ValidationError'

  /service-requests/{id}:
    get:
      tags: [ServiceRequests]
      summary: Get a single service request
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Service request details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/ServiceRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [ServiceRequests]
      summary: Update a service request
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateServiceRequestRequest'
      responses:
        '200':
          description: Service request updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/ServiceRequest'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [ServiceRequests]
      summary: Delete a service request
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Service request deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Documents
  # -------------------------------------------------------------------------
  /documents:
    get:
      tags: [Documents]
      summary: List documents
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Document'
    post:
      tags: [Documents]
      summary: Create a document
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDocumentRequest'
      responses:
        '201':
          description: Document created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Document'
        '422':
          $ref: '#/components/responses/ValidationError'

  /documents/{id}:
    get:
      tags: [Documents]
      summary: Get a single document
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Document details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Document'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Documents]
      summary: Update a document
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDocumentRequest'
      responses:
        '200':
          description: Document updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Document'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [Documents]
      summary: Delete a document
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Document deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Downloads
  # -------------------------------------------------------------------------
  /downloads:
    get:
      tags: [Downloads]
      summary: List available downloads
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: List of downloads (not paginated).
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Download'

  /downloads/{id}:
    get:
      tags: [Downloads]
      summary: Get a single download
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Download details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Download'
        '404':
          $ref: '#/components/responses/NotFound'

  /downloads/{id}/file:
    get:
      tags: [Downloads]
      summary: Get download file URL
      description: Checks license authorization before returning the URL.
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: File URL.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DownloadFileResponse'
        '403':
          $ref: '#/components/responses/Forbidden'

  # -------------------------------------------------------------------------
  # Tax Invoices
  # -------------------------------------------------------------------------
  /tax-invoices:
    get:
      tags: [TaxInvoices]
      summary: List tax invoices (NFSe)
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated list.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/TaxInvoice'
    post:
      tags: [TaxInvoices]
      summary: Create a tax invoice
      parameters:
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaxInvoiceRequest'
      responses:
        '201':
          description: Tax invoice created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/TaxInvoice'
        '422':
          $ref: '#/components/responses/ValidationError'

  /tax-invoices/{id}:
    get:
      tags: [TaxInvoices]
      summary: Get a single tax invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Tax invoice details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/TaxInvoice'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [TaxInvoices]
      summary: Update a tax invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTaxInvoiceRequest'
      responses:
        '200':
          description: Tax invoice updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/TaxInvoice'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags: [TaxInvoices]
      summary: Delete a tax invoice
      parameters:
        - $ref: '#/components/parameters/PathId'
        - $ref: '#/components/parameters/XMiddagOrganization'
      responses:
        '200':
          description: Tax invoice deleted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/DeletedResponse'

  # -------------------------------------------------------------------------
  # Sites
  # -------------------------------------------------------------------------
  /sites:
    get:
      tags: [Sites]
      summary: List current user's sites
      responses:
        '200':
          description: List of sites.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          data:
                            type: array
                            items:
                              $ref: '#/components/schemas/Site'
                          total:
                            type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'

  /sites/search:
    get:
      tags: [Sites]
      summary: Search sites by URL
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          description: Search term (URL fragment).
      responses:
        '200':
          description: Search results.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          data:
                            type: array
                            items:
                              $ref: '#/components/schemas/Site'
                          total:
                            type: integer
        '422':
          $ref: '#/components/responses/ValidationError'

  /sites/{id_number}:
    get:
      tags: [Sites]
      summary: Get site details
      parameters:
        - name: id_number
          in: path
          required: true
          schema:
            type: string
            pattern: '^[a-z0-9]+$'
      responses:
        '200':
          description: Site details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Site'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /sites/{id_number}/configure:
    post:
      tags: [Sites]
      summary: Configure a site (replaces Elementor form)
      parameters:
        - name: id_number
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigureSiteRequest'
      responses:
        '200':
          description: Site configured.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Site'
        '422':
          $ref: '#/components/responses/ValidationError'

  /sites/{id_number}/domain:
    put:
      tags: [Sites]
      summary: Set custom domain for a site
      parameters:
        - name: id_number
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetDomainRequest'
      responses:
        '200':
          description: Domain set.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Site'
        '422':
          $ref: '#/components/responses/ValidationError'

  /sites/{id_number}/install:
    post:
      tags: [Sites]
      summary: Trigger site installation (admin only)
      parameters:
        - name: id_number
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Installation triggered.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        additionalProperties: true
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /sites/{id_number}/support-login:
    post:
      tags: [Sites]
      summary: Get support login URL (admin only)
      parameters:
        - name: id_number
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Support login URL.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        additionalProperties: true
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  # -------------------------------------------------------------------------
  # Affiliates
  # -------------------------------------------------------------------------
  /affiliates/profile:
    get:
      tags: [Affiliates]
      summary: Get affiliate profile
      responses:
        '200':
          description: Affiliate profile.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/AffiliateProfile'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /affiliates/referrals:
    get:
      tags: [Affiliates]
      summary: List referrals
      parameters:
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated referrals.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /affiliates/commissions:
    get:
      tags: [Affiliates]
      summary: Get commission summary
      responses:
        '200':
          description: Commission summary.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/CommissionSummary'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /affiliates/payouts:
    get:
      tags: [Affiliates]
      summary: Get payout history
      parameters:
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Paginated payout history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  # -------------------------------------------------------------------------
  # Customers (Stripe)
  # -------------------------------------------------------------------------
  /customers/stripe/{id}:
    get:
      tags: [Customers]
      summary: Get Stripe customer (admin only)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            pattern: '^[a-zA-Z0-9_]+$'
          description: Stripe customer ID (e.g. cus_xxx).
      responses:
        '200':
          description: Stripe customer data.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/StripeCustomer'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [Customers]
      summary: Update Stripe customer (admin only)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: Stripe customer fields to update.
      responses:
        '200':
          description: Customer updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/StripeCustomer'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          description: Update failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /customers/by-stripe:
    get:
      tags: [Customers]
      summary: Find WP user by Stripe customer ID (admin only)
      parameters:
        - name: stripe_customerid
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: User found.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/FindUserByStripeResponse'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Jira
  # -------------------------------------------------------------------------
  /jira/projects:
    get:
      tags: [Jira]
      summary: List Jira projects (admin only)
      parameters:
        - name: start_at
          in: query
          schema:
            type: integer
            default: 0
        - name: max_results
          in: query
          schema:
            type: integer
            default: 50
      responses:
        '200':
          description: Jira projects.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/JiraProject'
        '403':
          $ref: '#/components/responses/Forbidden'
        '502':
          description: Jira API error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /jira/projects/{key}/issue-types:
    get:
      tags: [Jira]
      summary: Get issue types for a Jira project (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
            pattern: '^[A-Z0-9]+$'
      responses:
        '200':
          description: Issue types.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/JiraIssueType'
        '403':
          $ref: '#/components/responses/Forbidden'

  /jira/issues:
    post:
      tags: [Jira]
      summary: Create a Jira issue (admin only)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJiraIssueRequest'
      responses:
        '201':
          description: Issue created.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/JiraIssue'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /jira/issues/search:
    post:
      tags: [Jira]
      summary: Search Jira issues via JQL (admin only)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JiraSearchRequest'
      responses:
        '200':
          description: Search results.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/JiraIssue'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /jira/issues/{key}:
    get:
      tags: [Jira]
      summary: Get a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
            pattern: '^[A-Z]+-\d+$'
      responses:
        '200':
          description: Issue details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/JiraIssue'
        '403':
          $ref: '#/components/responses/Forbidden'
    put:
      tags: [Jira]
      summary: Update a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                fields:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Issue updated.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          key:
                            type: string
                          updated:
                            type: boolean
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      tags: [Jira]
      summary: Delete a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Issue deleted.
        '403':
          $ref: '#/components/responses/Forbidden'

  /jira/issues/{key}/transitions:
    get:
      tags: [Jira]
      summary: Get available transitions for a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Available transitions.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/JiraTransition'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Jira]
      summary: Transition a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransitionIssueRequest'
      responses:
        '200':
          description: Issue transitioned.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          key:
                            type: string
                          transitioned:
                            type: boolean
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /jira/issues/{key}/comments:
    get:
      tags: [Jira]
      summary: List comments on a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Comments.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/JiraComment'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Jira]
      summary: Add a comment to a Jira issue (admin only)
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddJiraCommentRequest'
      responses:
        '201':
          description: Comment added.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/JiraComment'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  # -------------------------------------------------------------------------
  # Webhooks
  # -------------------------------------------------------------------------
  /webhooks/stripe:
    post:
      tags: [Webhooks]
      summary: Stripe webhook (BR default)
      description: >
        Validates Stripe-Signature header before processing.
        No user authentication required.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Webhook processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
        '400':
          description: Invalid payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Invalid signature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /webhooks/stripe/br:
    post:
      tags: [Webhooks]
      summary: Stripe webhook (BR account)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Webhook processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
        '401':
          description: Invalid signature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /webhooks/stripe/global:
    post:
      tags: [Webhooks]
      summary: Stripe webhook (Global account)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Webhook processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
        '401':
          description: Invalid signature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

  /webhooks/hubspot:
    post:
      tags: [Webhooks]
      summary: HubSpot webhook
      description: >
        Validates X-HubSpot-Signature-V3 header before processing.
        No user authentication required.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                additionalProperties: true
      responses:
        '200':
          description: Webhook processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessEnvelope'
        '400':
          description: Invalid payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Invalid signature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'

# ===========================================================================
# Tags
# ===========================================================================
tags:
  - name: Auth
    description: Authentication, registration, password reset, account verification.
  - name: Organizations
    description: Organization (tenant) CRUD and CNPJ validation.
  - name: Collaborators
    description: Team member management, invite flow, and RBAC permissions.
  - name: Entitlements
    description: Central entitlement aggregator -- PLG, ENV, SVC, ORD, AFL, EDU classes.
  - name: Orders
    description: WooCommerce order adapter -- list, view, payment info, status update.
  - name: Invoices
    description: Stripe invoices -- CRUD, query by order/org, PDF download.
  - name: Quotes
    description: Quotes (proposals) with accept/reject lifecycle.
  - name: Licenses
    description: WC Software License adapter -- list, view, activate, deactivate.
  - name: Contracts
    description: Legal contracts CRUD.
  - name: Environments
    description: Managed hosting environments (Moodle, WordPress, custom).
  - name: Services
    description: Ongoing and project-based services.
  - name: ServiceRequests
    description: Service request tickets.
  - name: Documents
    description: Organization document management.
  - name: Downloads
    description: Product downloads with license authorization.
  - name: TaxInvoices
    description: Brazilian NFSe (Nota Fiscal de Servico) management.
  - name: Sites
    description: Managed site provisioning (middag.pro).
  - name: Affiliates
    description: Affiliate program -- profile, referrals, commissions, payouts.
  - name: Customers
    description: Stripe customer administration (admin only).
  - name: Jira
    description: Jira Cloud integration -- projects, issues, transitions, comments (admin only).
  - name: Webhooks
    description: Inbound webhooks from Stripe and HubSpot (signature-validated).
