openapi: 3.1.0
info:
  title: cStar API
  description: |
    REST API for cStar, the gamified helpdesk platform.

    ## Authentication
    All endpoints require an API key passed in the Authorization header:
    ```
    Authorization: Bearer sk_live_xxxxx
    ```

    There are two types of API keys:
    - **Secret keys** (`sk_live_*`): Full CRUD access. Keep these secure - never expose in client code!
    - **Publishable keys** (`pk_live_*`): Read-only access to public data. Safe to use in client code.

    ## Rate Limits
    - Secret keys: 120 requests per minute
    - Publishable keys: 60 requests per minute

    Rate limit info is returned in response headers:
    - `X-RateLimit-Limit`: Maximum requests per window
    - `X-RateLimit-Remaining`: Requests remaining
    - `X-RateLimit-Reset`: Unix timestamp when window resets
    - `X-RateLimit-Window`: Window duration in seconds

    ## For AI Agents
    This API follows consistent patterns:
    - All responses have `success`, `data`, and `meta` fields
    - Pagination uses `page` and `pageSize` query params
    - Filters are query params (e.g., `?status=open&priority=high`)
    - Errors include `code`, `message`, and optionally `field`

    ## Response Format
    ```json
    {
      "success": true,
      "data": { ... },
      "meta": {
        "requestId": "req_abc123",
        "timestamp": "2025-12-14T10:30:00Z"
      }
    }
    ```

  version: 2.0.0
  contact:
    name: cStar Support
    url: https://cstar.help/support
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://www.cstar.help/api/v1
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Tickets
    description: Support ticket management
  - name: Customers
    description: Customer management
  - name: Articles
    description: Knowledge base article management
  - name: Messages
    description: Ticket message management
  - name: Webhooks
    description: |
      Webhook management and event subscriptions.

      ## Verifying Webhook Signatures

      All webhook deliveries include a cryptographic signature in the `X-Signature` header.
      You **MUST** verify this signature to ensure the webhook came from cStar.

      ### Headers Sent with Each Delivery

      | Header | Description |
      |--------|-------------|
      | `x-cstar-signature` | HMAC-SHA256 signature of the request body |
      | `x-cstar-event` | Event type (e.g., `ticket.created`) |
      | `x-cstar-delivery` | Unique delivery ID for idempotency |

      ### Node.js / JavaScript
      ```javascript
      const crypto = require('crypto');

      function verifyWebhookSignature(rawBody, signature, secret) {
        const expected = 'sha256=' + crypto
          .createHmac('sha256', secret)
          .update(rawBody)
          .digest('hex');

        return crypto.timingSafeEqual(
          Buffer.from(signature),
          Buffer.from(expected)
        );
      }
      ```

      ### Python
      ```python
      import hmac, hashlib

      def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
          expected = 'sha256=' + hmac.new(
              secret.encode(), raw_body, hashlib.sha256
          ).hexdigest()
          return hmac.compare_digest(signature, expected)
      ```

      ### Go
      ```go
      func verifySignature(body []byte, signature, secret string) bool {
          mac := hmac.New(sha256.New, []byte(secret))
          mac.Write(body)
          expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
          return hmac.Equal([]byte(signature), []byte(expected))
      }
      ```
  - name: Members
    description: Team member and invite management
  - name: Settings
    description: Team settings, statuses, quick replies, business hours, widget
  - name: Notifications
    description: User notification management
  - name: Attachments
    description: File attachment management
  - name: Custom Fields
    description: Custom field definitions
  - name: SLA
    description: SLA rule management
  - name: Analytics
    description: Team analytics and CSAT data
  - name: Bulk
    description: Bulk operations on resources
  - name: Export
    description: Data export
  - name: Import
    description: Data import
  - name: Audit Log
    description: Audit trail
  - name: Search
    description: Cross-resource search
  - name: Tags
    description: Tag management
  - name: Activity
    description: Activity feed
  - name: Customer Groups
    description: Customer segments/groups
  - name: Game
    description: Gamification system (player stats, boss battles, achievements, quests, puzzles, social, cosmetics, seasons, skills)
  - name: AI
    description: AI-powered actions (auto-tag, suggest-reply, summarize, improve)
  - name: Views
    description: Saved views / filter presets
  - name: Billing
    description: Subscription and billing portal
  - name: Automations
    description: Automation rule management
  - name: Community
    description: Community forum posts and topics
  - name: Categories
    description: Article category management
  - name: Mentions
    description: "@mention tracking"
  - name: API Keys
    description: API key management
  - name: Events
    description: Real-time event streaming (SSE)
  - name: Logs
    description: API request logs
  - name: Auth
    description: Authentication and key validation

paths:
  # ── Auth ──
  /auth/whoami:
    get:
      operationId: whoami
      summary: Validate API key and get team info
      description: Returns the team and key metadata associated with the Bearer token. Used by CLI login/status.
      tags: [Auth]
      responses:
        '200':
          description: Key is valid
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: object
                    properties:
                      teamId: { type: string, format: uuid }
                      teamName: { type: string }
                      teamSlug: { type: string }
                      keyId: { type: string }
                      keyType: { type: string, enum: [secret, publishable] }
                      environment: { type: string }
                      createdAt: { type: string, format: date-time }
                      subscriptionStatus: { type: string }
        '401':
          $ref: '#/components/responses/Unauthorized'

  /teams/{teamId}/tickets:
    get:
      operationId: listTickets
      summary: List all tickets
      description: |
        Retrieve a paginated list of tickets for the team.

        **Common filters:**
        - `status`: Filter by ticket status (new, open, pending, resolved, closed)
        - `priority`: Filter by priority (low, normal, high, urgent)
        - `customerId`: Filter by customer
        - `search`: Search in title and customer name

        **AI Agent tip:** Start with this endpoint to understand what tickets exist.
      tags:
        - Tickets
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: status
          in: query
          description: Filter by ticket status
          schema:
            type: string
            enum: [new, open, pending, resolved, closed]
        - name: priority
          in: query
          description: Filter by priority level
          schema:
            type: string
            enum: [low, normal, high, urgent]
        - name: customerId
          in: query
          description: Filter by customer ID
          schema:
            type: string
            format: uuid
        - name: search
          in: query
          description: Search in title and customer name
          schema:
            type: string
        - name: assignedTo
          in: query
          description: Filter by assigned team member ID
          schema:
            type: string
            format: uuid
        - name: expand[]
          in: query
          description: Include related objects in the response. Supports `customer` and `messages`.
          schema:
            type: array
            items:
              type: string
              enum: [customer, messages]
          style: form
          explode: true
      responses:
        '200':
          description: List of tickets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TicketListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'

    post:
      operationId: createTicket
      summary: Create a new ticket
      description: |
        Create a new support ticket.

        **Required fields:**
        - `title`: Brief description of the issue

        **Optional fields:**
        - `priority`: low, normal (default), high, urgent
        - `customerId`: Link to existing customer
        - `customerName`: Customer name (used if no customerId)
        - `tags`: Array of tag strings
        - `notes`: Internal notes
      tags:
        - Tickets
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTicketRequest'
      responses:
        '201':
          description: Ticket created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TicketResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'

  /teams/{teamId}/tickets/{ticketId}:
    get:
      operationId: getTicket
      summary: Get a single ticket
      description: Retrieve detailed information about a specific ticket, including messages.
      tags:
        - Tickets
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/ticketId'
      responses:
        '200':
          description: Ticket details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TicketDetailResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateTicket
      summary: Update a ticket
      description: |
        Update ticket properties. Only provided fields will be updated.

        **Status changes:**
        Setting status to "closed" or "resolved" will set the closedAt timestamp.
      tags:
        - Tickets
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/ticketId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTicketRequest'
      responses:
        '200':
          description: Ticket updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TicketResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    delete:
      operationId: deleteTicket
      summary: Delete a ticket
      description: Permanently delete a ticket and all associated messages.
      tags:
        - Tickets
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/ticketId'
      responses:
        '200':
          description: Ticket deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /teams/{teamId}/tickets/{ticketId}/messages:
    get:
      operationId: listMessages
      summary: List ticket messages
      description: Get all messages for a ticket in chronological order.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/ticketId'
      responses:
        '200':
          description: List of messages
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageListResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    post:
      operationId: addMessage
      summary: Add a message to a ticket
      description: |
        Add a new message to a ticket conversation.

        **Sender types:**
        - `customer`: Message from the customer
        - `agent`: Message from a support agent (default)
        - `system`: Automated system message

        Adding an agent message will set `firstResponseAt` if this is the first agent reply.
      tags:
        - Messages
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/ticketId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMessageRequest'
      responses:
        '201':
          description: Message added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /teams/{teamId}/customers:
    get:
      operationId: listCustomers
      summary: List all customers
      description: Retrieve a paginated list of customers for the team.
      tags:
        - Customers
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: status
          in: query
          description: Filter by customer status
          schema:
            type: string
        - name: sentiment
          in: query
          description: Filter by sentiment
          schema:
            type: string
        - name: search
          in: query
          description: Search in name and email
          schema:
            type: string
        - name: tag
          in: query
          description: Filter by tag
          schema:
            type: string
      responses:
        '200':
          description: List of customers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerListResponse'

    post:
      operationId: createCustomer
      summary: Create a new customer
      description: |
        Create a new customer record.

        **Required fields:**
        - `name`: Customer's name
        - `email`: Customer's email (must be unique within team)
      tags:
        - Customers
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomerRequest'
      responses:
        '201':
          description: Customer created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerResponse'
        '409':
          description: Customer with this email already exists

  /teams/{teamId}/customers/{customerId}:
    get:
      operationId: getCustomer
      summary: Get a single customer
      description: Retrieve detailed information about a customer, including recent tickets.
      tags:
        - Customers
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/customerId'
      responses:
        '200':
          description: Customer details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerDetailResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateCustomer
      summary: Update a customer
      tags:
        - Customers
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/customerId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomerRequest'
      responses:
        '200':
          description: Customer updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerResponse'

    delete:
      operationId: deleteCustomer
      summary: Delete a customer
      tags:
        - Customers
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/customerId'
      responses:
        '200':
          description: Customer deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /teams/{teamId}/articles:
    get:
      operationId: listArticles
      summary: List all articles
      description: Retrieve a paginated list of knowledge base articles.
      tags:
        - Articles
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: category
          in: query
          description: Filter by category
          schema:
            type: string
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum: [draft, published, archived]
        - name: isPublic
          in: query
          description: Filter by public visibility
          schema:
            type: boolean
        - name: search
          in: query
          description: Search in title and content
          schema:
            type: string
      responses:
        '200':
          description: List of articles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArticleListResponse'

    post:
      operationId: createArticle
      summary: Create a new article
      description: |
        Create a new knowledge base article.

        **Required fields:**
        - `title`: Article title

        A URL-friendly slug will be auto-generated from the title if not provided.
      tags:
        - Articles
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateArticleRequest'
      responses:
        '201':
          description: Article created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArticleResponse'

  /teams/{teamId}/articles/{articleId}:
    get:
      operationId: getArticle
      summary: Get a single article
      tags:
        - Articles
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/articleId'
      responses:
        '200':
          description: Article details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArticleResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateArticle
      summary: Update an article
      tags:
        - Articles
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/articleId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateArticleRequest'
      responses:
        '200':
          description: Article updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ArticleResponse'

    delete:
      operationId: deleteArticle
      summary: Delete an article
      tags:
        - Articles
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/articleId'
      responses:
        '200':
          description: Article deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /teams/{teamId}/webhooks:
    get:
      operationId: listWebhooks
      summary: List all webhooks
      description: Retrieve all webhooks configured for the team.
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: List of webhooks
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookListResponse'

    post:
      operationId: createWebhook
      summary: Create a new webhook
      description: |
        Create a new webhook endpoint to receive event notifications.

        **Required fields:**
        - `name`: A descriptive name
        - `url`: The endpoint URL (must be HTTPS)
        - `events`: Array of event types to subscribe to

        A signing secret will be generated and returned. **Store it securely** - it won't be shown again.
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '201':
          description: Webhook created (includes secret - save it!)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookCreatedResponse'

  /teams/{teamId}/webhooks/{webhookId}:
    get:
      operationId: getWebhook
      summary: Get a single webhook
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/webhookId'
      responses:
        '200':
          description: Webhook details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateWebhook
      summary: Update a webhook
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/webhookId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookRequest'
      responses:
        '200':
          description: Webhook updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookResponse'

    delete:
      operationId: deleteWebhook
      summary: Delete a webhook
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/webhookId'
      responses:
        '200':
          description: Webhook deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'

  /teams/{teamId}/webhooks/{webhookId}/test:
    post:
      operationId: testWebhook
      summary: Send a test event to a webhook
      description: |
        Sends a test delivery to the webhook endpoint to verify configuration.
        Returns the delivery result including response status and timing.
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/webhookId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  description: Event type to simulate (defaults to ping)
                  example: ticket.created
      responses:
        '200':
          description: Test delivery result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResponse'

  /teams/{teamId}/webhooks/subscribe:
    post:
      operationId: subscribeWebhook
      summary: Subscribe to events (REST Hooks)
      description: |
        REST Hooks compliant subscription endpoint for Zapier and similar platforms.
        Creates a webhook subscription programmatically.
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - targetUrl
                - event
              properties:
                targetUrl:
                  type: string
                  format: uri
                  description: URL to receive webhook events
                event:
                  type: string
                  description: Event type to subscribe to
      responses:
        '201':
          description: Subscription created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      targetUrl:
                        type: string
                      event:
                        type: string

  /teams/{teamId}/webhooks/subscribe/{id}:
    delete:
      operationId: unsubscribeWebhook
      summary: Unsubscribe from events (REST Hooks)
      description: REST Hooks compliant unsubscribe endpoint. Accepts UUID or prefixed webhook ID (whk_xxx).
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: id
          in: path
          required: true
          description: Webhook subscription ID (UUID or whk_xxx)
          schema:
            type: string
      responses:
        '204':
          description: Subscription removed

  /teams/{teamId}/webhooks/sample/{event}:
    get:
      operationId: getWebhookSample
      summary: Get sample event data
      description: |
        Returns sample payload data for a specific event type.
        Used by Zapier and other platforms to understand the data structure.
      tags:
        - Webhooks
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: event
          in: path
          required: true
          schema:
            type: string
          description: Event type (e.g., ticket.created)
      responses:
        '200':
          description: Sample event data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      type: object
                      description: Sample event payload

  # ── Members ──
  /teams/{teamId}/members:
    get:
      operationId: listMembers
      summary: List team members
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: role
          in: query
          schema: { type: string }
      responses:
        '200': { description: List of members }

  /teams/{teamId}/members/{memberId}:
    get:
      operationId: getMember
      summary: Get a team member
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: memberId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Member details }
    patch:
      operationId: updateMember
      summary: Update member role
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: memberId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                role: { type: string }
      responses:
        '200': { description: Updated member }
    delete:
      operationId: removeMember
      summary: Remove a member
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: memberId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Member removed }

  /teams/{teamId}/invites:
    get:
      operationId: listInvites
      summary: List pending invites
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of invites }
    post:
      operationId: createInvite
      summary: Create and send an invite
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email: { type: string }
                role: { type: string }
      responses:
        '201': { description: Invite created }

  /teams/{teamId}/invites/{inviteId}:
    delete:
      operationId: revokeInvite
      summary: Revoke a pending invite
      tags: [Members]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: inviteId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Invite revoked }

  # ── Settings ──
  /teams/{teamId}/settings:
    get:
      operationId: getSettings
      summary: Get all team settings
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Team settings }
    patch:
      operationId: updateSettings
      summary: Update team settings
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Updated settings }

  /teams/{teamId}/settings/statuses:
    get:
      operationId: listStatuses
      summary: List ticket status configurations
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of statuses }
    post:
      operationId: createStatus
      summary: Create a custom status
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: Status created }

  /teams/{teamId}/settings/statuses/{statusKey}:
    patch:
      operationId: updateStatus
      summary: Update a status
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: statusKey
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated status }
    delete:
      operationId: deleteStatus
      summary: Delete a custom status
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: statusKey
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Status deleted }

  /teams/{teamId}/settings/statuses/reorder:
    put:
      operationId: reorderStatuses
      summary: Reorder statuses
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [keys]
              properties:
                keys:
                  type: array
                  items: { type: string }
      responses:
        '200': { description: Statuses reordered }

  /teams/{teamId}/settings/quick-replies:
    get:
      operationId: listQuickReplies
      summary: List quick reply templates
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of quick replies }
    post:
      operationId: createQuickReply
      summary: Create a quick reply
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: Quick reply created }

  /teams/{teamId}/settings/quick-replies/{replyId}:
    patch:
      operationId: updateQuickReply
      summary: Update a quick reply
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: replyId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated quick reply }
    delete:
      operationId: deleteQuickReply
      summary: Delete a quick reply
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: replyId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Quick reply deleted }

  /teams/{teamId}/settings/quick-replies/reorder:
    put:
      operationId: reorderQuickReplies
      summary: Reorder quick replies
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items: { type: string }
      responses:
        '200': { description: Quick replies reordered }

  /teams/{teamId}/settings/business-hours:
    get:
      operationId: getBusinessHours
      summary: Get business hours
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Business hours config }
    patch:
      operationId: updateBusinessHours
      summary: Update business hours
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Updated business hours }

  /teams/{teamId}/settings/widget:
    get:
      operationId: getWidgetSettings
      summary: Get widget settings
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Widget settings }
    patch:
      operationId: updateWidgetSettings
      summary: Update widget settings
      tags: [Settings]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Updated widget settings }

  # ── Notifications ──
  /teams/{teamId}/notifications:
    get:
      operationId: listNotifications
      summary: List notifications
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: unreadOnly
          in: query
          schema: { type: boolean }
      responses:
        '200': { description: List of notifications }

  /teams/{teamId}/notifications/{notificationId}:
    delete:
      operationId: deleteNotification
      summary: Delete a notification
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: notificationId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Notification deleted }

  /teams/{teamId}/notifications/{notificationId}/read:
    patch:
      operationId: markNotificationRead
      summary: Mark a single notification as read
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: notificationId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Notification marked read }

  /teams/{teamId}/notifications/unread-count:
    get:
      operationId: getUnreadNotificationCount
      summary: Get unread notification count
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Unread count }

  /teams/{teamId}/notifications/mark-all-read:
    post:
      operationId: markAllNotificationsRead
      summary: Mark all notifications as read
      tags: [Notifications]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: All marked read }

  # ── Attachments ──
  /teams/{teamId}/attachments:
    post:
      operationId: uploadAttachment
      summary: Upload an attachment
      tags: [Attachments]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file: { type: string, format: binary }
      responses:
        '201': { description: Attachment uploaded }

  /teams/{teamId}/attachments/{path}:
    get:
      operationId: getAttachment
      summary: Get an attachment by path
      tags: [Attachments]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: path
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Attachment data }
    delete:
      operationId: deleteAttachment
      summary: Delete an attachment
      tags: [Attachments]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: path
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Attachment deleted }

  # ── Custom Fields ──
  /teams/{teamId}/custom-fields:
    get:
      operationId: listCustomFields
      summary: List custom field definitions
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: entityType
          in: query
          schema:
            type: string
            enum: [tickets, customers]
      responses:
        '200': { description: List of custom fields }
    post:
      operationId: createCustomField
      summary: Create a custom field
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: Custom field created }

  /teams/{teamId}/custom-fields/{fieldId}:
    patch:
      operationId: updateCustomField
      summary: Update a custom field
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: fieldId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated custom field }
    delete:
      operationId: deleteCustomField
      summary: Delete a custom field
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: fieldId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Custom field deleted }

  /teams/{teamId}/custom-fields/reorder:
    put:
      operationId: reorderCustomFields
      summary: Reorder custom fields
      tags: [Custom Fields]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items: { type: string }
      responses:
        '200': { description: Fields reordered }

  # ── SLA Rules ──
  /teams/{teamId}/sla/rules:
    get:
      operationId: listSlaRules
      summary: List SLA rules
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of SLA rules }
    post:
      operationId: createSlaRule
      summary: Create an SLA rule
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: SLA rule created }

  /teams/{teamId}/sla/rules/{ruleId}:
    patch:
      operationId: updateSlaRule
      summary: Update or toggle an SLA rule
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated SLA rule }
    delete:
      operationId: deleteSlaRule
      summary: Delete an SLA rule
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: SLA rule deleted }

  /teams/{teamId}/sla/rules/reorder:
    put:
      operationId: reorderSlaRules
      summary: Reorder SLA rules
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items: { type: string }
                  description: Ordered array of rule IDs
      responses:
        '200': { description: Rules reordered }

  /teams/{teamId}/sla/metrics:
    get:
      operationId: getSlaMetrics
      summary: Get SLA compliance metrics
      description: Returns SLA compliance rates, breach counts, and response/resolution time distributions.
      tags: [SLA]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
        - name: startDate
          in: query
          schema: { type: string, format: date }
        - name: endDate
          in: query
          schema: { type: string, format: date }
        - name: agentId
          in: query
          schema: { type: string, format: uuid }
      responses:
        '200': { description: SLA metrics }

  # ── Analytics ──
  /teams/{teamId}/analytics:
    get:
      operationId: getAnalytics
      summary: Get analytics data
      description: |
        Returns analytics based on the `type` parameter:
        - `overview` (default): Ticket counts, customer metrics, CSAT summary
        - `tickets`: Detailed ticket volume and status breakdown
        - `performance`: Per-agent response times and resolution stats
        - `library`: Article views and usage metrics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: type
          in: query
          schema:
            type: string
            enum: [overview, tickets, performance, library]
            default: overview
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month, quarter]
            default: week
        - name: agentId
          in: query
          description: Filter to a specific agent (for performance type)
          schema:
            type: string
            format: uuid
      responses:
        '200': { description: Analytics data }

  # ── CSAT ──
  /teams/{teamId}/csat/settings:
    get:
      operationId: getCsatSettings
      summary: Get CSAT configuration
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: CSAT settings }
    patch:
      operationId: updateCsatSettings
      summary: Update CSAT configuration
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Updated CSAT settings }

  /teams/{teamId}/csat/stats:
    get:
      operationId: getCsatStats
      summary: Get CSAT statistics
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: CSAT stats }

  /teams/{teamId}/csat/surveys:
    get:
      operationId: listCsatSurveys
      summary: List CSAT surveys
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: completed
          in: query
          schema: { type: boolean }
        - name: agentId
          in: query
          schema: { type: string, format: uuid }
      responses:
        '200': { description: CSAT surveys }
    post:
      operationId: createCsatSurvey
      summary: Create and send a CSAT survey
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ticketId, customerId]
              properties:
                ticketId: { type: string, format: uuid }
                customerId: { type: string, format: uuid }
      responses:
        '201': { description: Survey created }

  /teams/{teamId}/csat/surveys/{surveyId}:
    get:
      operationId: getCsatSurvey
      summary: Get a single CSAT survey
      tags: [Analytics]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: surveyId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Survey details }

  # ── Bulk Operations ──
  /teams/{teamId}/bulk:
    post:
      operationId: bulkOperation
      summary: Perform bulk update or delete
      description: |
        Bulk update or delete tickets, customers, or articles.
        - `action`: "update" or "delete"
        - `resource`: "tickets", "customers", or "articles"
        - `ids`: Array of resource IDs
        - `data`: Update fields (for update action only)
      tags: [Bulk]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action, resource, ids]
              properties:
                action:
                  type: string
                  enum: [update, delete]
                resource:
                  type: string
                  enum: [tickets, customers, articles]
                ids:
                  type: array
                  items: { type: string }
                data:
                  type: object
      responses:
        '200': { description: Bulk operation result }

  # ── Export ──
  /teams/{teamId}/export:
    post:
      operationId: createExport
      summary: Export data (JSON or CSV)
      description: Synchronous export. Max 10,000 rows per resource.
      tags: [Export]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [resources]
              properties:
                resources:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                        enum: [tickets, customers, articles]
                      filters:
                        type: object
                format:
                  type: string
                  enum: [json, csv]
      responses:
        '200': { description: Exported data }

  # ── Import ──
  /teams/{teamId}/import:
    get:
      operationId: listImportLogs
      summary: List import history logs
      tags: [Import]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Import logs }
    post:
      operationId: createImport
      summary: Import records (max 500)
      tags: [Import]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [resource, records]
              properties:
                resource:
                  type: string
                  enum: [tickets, customers, articles]
                records:
                  type: array
                  items: { type: object }
                mergeStrategy:
                  type: string
                  enum: [skip, overwrite, merge]
      responses:
        '200': { description: Import result }

  # ── Audit Log ──
  /teams/{teamId}/audit-log:
    get:
      operationId: listAuditLog
      summary: List audit log entries
      tags: [Audit Log]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: action
          in: query
          schema: { type: string }
        - name: resourceType
          in: query
          schema: { type: string }
      responses:
        '200': { description: Audit log entries }

  # ── Search ──
  /teams/{teamId}/search:
    get:
      operationId: searchAll
      summary: Search across tickets, customers, and articles
      tags: [Search]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: q
          in: query
          required: true
          schema: { type: string }
        - name: types
          in: query
          description: Comma-separated resource types
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer }
      responses:
        '200': { description: Search results }

  # ── Tags ──
  /teams/{teamId}/tags:
    get:
      operationId: listTags
      summary: List all tags with usage counts
      tags: [Tags]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of tags }
    post:
      operationId: mergeTags
      summary: Merge two tags into one
      description: |
        Merge one tag into another across all resources (tickets, customers, articles).
        Requires `action: 'merge'`, `fromTag`, and `toTag` in the request body.
        All items tagged with `fromTag` will be retagged with `toTag`.
      tags: [Tags]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action, fromTag, toTag]
              properties:
                action:
                  type: string
                  enum: [merge]
                fromTag:
                  type: string
                  description: The tag to merge from (will be removed)
                toTag:
                  type: string
                  description: The tag to merge into (will be kept)
      responses:
        '200': { description: Tags merged successfully }
    delete:
      operationId: deleteTag
      summary: Delete a tag
      tags: [Tags]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
      responses:
        '200': { description: Tag deleted }

  # ── Activity ──
  /teams/{teamId}/activity:
    get:
      operationId: listActivity
      summary: List recent activity events
      tags: [Activity]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: Activity events }

  # ── Customer Groups ──
  /teams/{teamId}/customer-groups:
    get:
      operationId: listCustomerGroups
      summary: List customer groups
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of groups }
    post:
      operationId: createCustomerGroup
      summary: Create a customer group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: Group created }

  /teams/{teamId}/customer-groups/{groupId}:
    get:
      operationId: getCustomerGroup
      summary: Get a customer group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Group details }
    patch:
      operationId: updateCustomerGroup
      summary: Update a customer group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated group }
    delete:
      operationId: deleteCustomerGroup
      summary: Delete a customer group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Group deleted }

  /teams/{teamId}/customer-groups/{groupId}/members:
    get:
      operationId: listCustomerGroupMembers
      summary: List members of a customer group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: Group members }
    post:
      operationId: addCustomerGroupMember
      summary: Add customer(s) to a group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [customerIds]
              properties:
                customerIds:
                  type: array
                  items: { type: string, format: uuid }
      responses:
        '200': { description: Members added }
    delete:
      operationId: removeCustomerGroupMembers
      summary: Remove customer(s) from a group
      tags: [Customer Groups]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: groupId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [customerIds]
              properties:
                customerIds:
                  type: array
                  items: { type: string, format: uuid }
      responses:
        '200': { description: Members removed }

  # ── Game: Player ──
  /teams/{teamId}/game/player:
    get:
      operationId: getPlayerStats
      summary: Get current player stats
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          schema: { type: string }
      responses:
        '200': { description: Player stats }

  /teams/{teamId}/game/player/{memberId}:
    get:
      operationId: getPlayerByMember
      summary: Get player stats by membership ID
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: memberId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Player stats }

  # ── Game: Boss ──
  /teams/{teamId}/game/boss:
    get:
      operationId: getBossState
      summary: Get current boss battle state
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Boss state }

  /teams/{teamId}/game/boss/active:
    get:
      operationId: isBossActive
      summary: Check if a boss is currently active
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Active status }

  /teams/{teamId}/game/boss/spawn:
    post:
      operationId: spawnBoss
      summary: Spawn a new boss
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                difficulty:
                  type: string
                  enum: [easy, medium, hard]
      responses:
        '201': { description: Boss spawned }

  /teams/{teamId}/game/boss/damage:
    post:
      operationId: dealBossDamage
      summary: Deal damage to current boss
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userId, ticketId]
              properties:
                userId: { type: string }
                ticketId: { type: string }
      responses:
        '200': { description: Damage result }

  /teams/{teamId}/game/boss/defeats:
    get:
      operationId: listBossDefeats
      summary: List boss defeat history
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: Defeat history }

  /teams/{teamId}/game/boss/defeats/{defeatId}:
    get:
      operationId: getBossDefeat
      summary: Get a single boss defeat
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: defeatId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Defeat details }

  # ── Game: Leaderboard ──
  /teams/{teamId}/game/leaderboard:
    get:
      operationId: getLeaderboard
      summary: Get team leaderboard
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: period
          in: query
          schema:
            type: string
            enum: [daily, weekly, allTime]
      responses:
        '200': { description: Leaderboard entries }

  /teams/{teamId}/game/leaderboard/me:
    get:
      operationId: getMyRank
      summary: Get current user leaderboard position
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: User rank }

  # ── Game: Achievements ──
  /teams/{teamId}/game/achievements:
    get:
      operationId: listAchievements
      summary: List all achievements with progress
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
        - name: category
          in: query
          schema: { type: string }
      responses:
        '200': { description: Achievements with progress }

  /teams/{teamId}/game/achievements/unlocked:
    get:
      operationId: listUnlockedAchievements
      summary: List unlocked achievements only
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Unlocked achievements }

  /teams/{teamId}/game/achievements/{achievementId}:
    get:
      operationId: getAchievement
      summary: Get a single achievement
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: achievementId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Achievement details }

  /teams/{teamId}/game/achievements/recent:
    get:
      operationId: getRecentAchievements
      summary: Get recently unlocked achievements across the team
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: limit
          in: query
          schema: { type: integer, default: 10, maximum: 50 }
      responses:
        '200': { description: Recent achievements }

  # ── Game: Quests ──
  /teams/{teamId}/game/quests/today:
    get:
      operationId: getTodayQuests
      summary: Get today's daily quests
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          schema: { type: string }
      responses:
        '200': { description: Today's quests }

  /teams/{teamId}/game/quests/history:
    get:
      operationId: getQuestHistory
      summary: Get quest history
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Quest history }

  # ── Game: Puzzles ──
  /teams/{teamId}/game/puzzles/today:
    get:
      operationId: getTodayPuzzle
      summary: Get today's puzzle
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Today's puzzle }

  /teams/{teamId}/game/puzzles/complete:
    post:
      operationId: completePuzzle
      summary: Submit puzzle answer
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userId, answer]
              properties:
                userId: { type: string }
                answer: { type: string }
      responses:
        '200': { description: Puzzle result }

  /teams/{teamId}/game/puzzles/streak:
    get:
      operationId: getPuzzleStreak
      summary: Get puzzle streak
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Puzzle streak }

  /teams/{teamId}/game/puzzles/leaderboard:
    get:
      operationId: getPuzzleLeaderboard
      summary: Get puzzle leaderboard
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Puzzle leaderboard }

  # ── Game: One-Ups ──
  /teams/{teamId}/game/one-ups:
    get:
      operationId: listOneUps
      summary: List one-ups (teammate kudos)
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: One-ups list }
    post:
      operationId: sendOneUp
      summary: Send a one-up
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [recipientId, userId]
              properties:
                recipientId: { type: string }
                userId: { type: string }
                message: { type: string }
      responses:
        '201': { description: One-up sent }

  /teams/{teamId}/game/one-ups/remaining:
    get:
      operationId: getOneUpRemaining
      summary: Get remaining free one-ups today
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Remaining count }

  # ── Game: High Fives ──
  /teams/{teamId}/game/high-fives:
    get:
      operationId: listHighFives
      summary: List high fives (team-to-team kudos)
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: High fives list }
    post:
      operationId: sendHighFive
      summary: Send a high five to another team
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [recipientTeamId, userId]
              properties:
                recipientTeamId: { type: string }
                userId: { type: string }
                message: { type: string }
      responses:
        '201': { description: High five sent }

  /teams/{teamId}/game/high-fives/can-send/{recipientTeamId}:
    get:
      operationId: canSendHighFive
      summary: Check if you can send a high five to a team
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: recipientTeamId
          in: path
          required: true
          schema: { type: string }
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Can-send status }

  # ── Game: Cosmetics ──
  /teams/{teamId}/game/cosmetics:
    get:
      operationId: listCosmetics
      summary: List unlocked cosmetics
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Unlocked cosmetics }

  /teams/{teamId}/game/cosmetics/preferences:
    get:
      operationId: getCosmeticPreferences
      summary: Get cosmetic preferences
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Cosmetic preferences }
    patch:
      operationId: updateCosmeticPreferences
      summary: Update cosmetic preferences
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Updated preferences }

  # ── Game: Seasons ──
  /teams/{teamId}/game/seasons:
    get:
      operationId: listSeasons
      summary: List all seasons
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Season list }

  /teams/{teamId}/game/seasons/current:
    get:
      operationId: getCurrentSeason
      summary: Get current active season
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Current season }

  /teams/{teamId}/game/seasons/{seasonId}/stats:
    get:
      operationId: getSeasonStats
      summary: Get user stats for a season
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: seasonId
          in: path
          required: true
          schema: { type: string }
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Season stats }

  # ── Game: Skills ──
  /teams/{teamId}/game/skills:
    get:
      operationId: getSkillTree
      summary: Get skill tree with allocations
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string }
      responses:
        '200': { description: Skill tree }

  /teams/{teamId}/game/skills/allocate:
    post:
      operationId: allocateSkillPoint
      summary: Allocate a skill point
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [skillId, userId]
              properties:
                skillId: { type: string }
                userId: { type: string }
      responses:
        '200': { description: Allocation result }

  /teams/{teamId}/game/skills/reset:
    post:
      operationId: resetSkills
      summary: Reset all skill allocations
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userId]
              properties:
                userId: { type: string }
      responses:
        '200': { description: Skills reset }

  # ── Game: Config ──
  /teams/{teamId}/game/config:
    get:
      operationId: getGameConfig
      summary: Get game balance constants
      description: Returns all game balance constants (XP, leveling, streaks, combos, boss difficulty) so custom dashboards can display game mechanics accurately.
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Game config }

  # ── Game: Activity Feed ──
  /teams/{teamId}/game/activity:
    get:
      operationId: getGameActivity
      summary: Get team activity feed
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: limit
          in: query
          schema: { type: integer, default: 50, maximum: 100 }
        - name: before
          in: query
          description: Cursor — activity ID to paginate before
          schema: { type: string }
      responses:
        '200': { description: Activity feed }

  /teams/{teamId}/game/activity/member/{memberId}:
    get:
      operationId: getMemberGameActivity
      summary: Get activity feed for a specific member
      tags: [Game]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: memberId
          in: path
          required: true
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 50, maximum: 100 }
        - name: before
          in: query
          schema: { type: string }
      responses:
        '200': { description: Member activity feed }

  # ── AI ──
  /teams/{teamId}/ai:
    post:
      operationId: runAiAction
      summary: Run an AI action
      description: |
        Available actions:
        - `auto-tag`: Auto-tag a ticket based on content
        - `suggest-reply`: Generate a reply suggestion
        - `summarize`: Summarize a ticket conversation
        - `improve`: Improve draft text
      tags: [AI]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action:
                  type: string
                  enum: [auto-tag, suggest-reply, summarize, improve]
                ticketId: { type: string }
                text: { type: string }
      responses:
        '200': { description: AI result }

  # ── Views ──
  /teams/{teamId}/views:
    get:
      operationId: listViews
      summary: List saved views
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: entityType
          in: query
          schema:
            type: string
            enum: [tickets, customers, articles, community_posts]
      responses:
        '200': { description: List of views }
    post:
      operationId: createView
      summary: Create a saved view
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, entityType]
              properties:
                name: { type: string }
                entityType:
                  type: string
                  enum: [tickets, customers, articles, community_posts]
                filters: { type: object }
                sortField: { type: string }
                sortDirection:
                  type: string
                  enum: [asc, desc]
                visibility:
                  type: string
                  enum: [private, team]
      responses:
        '201': { description: View created }

  /teams/{teamId}/views/{viewId}:
    get:
      operationId: getView
      summary: Get a saved view
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: viewId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: View details }
    patch:
      operationId: updateView
      summary: Update a saved view
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: viewId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated view }
    delete:
      operationId: deleteView
      summary: Delete a saved view
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: viewId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: View deleted }

  /teams/{teamId}/views/{viewId}/execute:
    get:
      operationId: executeView
      summary: Execute a view — returns filtered results
      tags: [Views]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: viewId
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: Filtered results }

  # ── Billing ──
  /teams/{teamId}/billing/subscription:
    get:
      operationId: getSubscription
      summary: Get subscription status
      tags: [Billing]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Subscription details }

  /teams/{teamId}/billing/portal:
    post:
      operationId: createBillingPortal
      summary: Create a Stripe billing portal session
      tags: [Billing]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                returnUrl: { type: string, format: uri }
      responses:
        '200': { description: Portal URL }

  # ── Automations ──
  /teams/{teamId}/automations:
    get:
      operationId: listAutomations
      summary: List automation rules
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: Automation rules }
    post:
      operationId: createAutomation
      summary: Create an automation rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '201': { description: Rule created }

  /teams/{teamId}/automations/{ruleId}:
    get:
      operationId: getAutomation
      summary: Get an automation rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Rule details }
    patch:
      operationId: updateAutomation
      summary: Update an automation rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Updated rule }
    delete:
      operationId: deleteAutomation
      summary: Delete an automation rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Rule deleted }

  /teams/{teamId}/automations/{ruleId}/toggle:
    patch:
      operationId: toggleAutomation
      summary: Enable or disable an automation rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200': { description: Rule toggled }

  /teams/{teamId}/automations/{ruleId}/test:
    post:
      operationId: testAutomation
      summary: Dry-run an automation rule against sample data
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId: { type: string, format: uuid }
      responses:
        '200': { description: Test result }

  /teams/{teamId}/automations/{ruleId}/executions:
    get:
      operationId: listAutomationRuleExecutions
      summary: List execution history for a specific rule
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: ruleId
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: Rule execution history }

  /teams/{teamId}/automations/evaluate:
    post:
      operationId: evaluateAutomationConditions
      summary: Test conditions against sample data
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [conditions, data]
              properties:
                conditions: { type: array, items: { type: object } }
                data: { type: object }
      responses:
        '200': { description: Evaluation result }

  /teams/{teamId}/automations/executions:
    get:
      operationId: listAllAutomationExecutions
      summary: List all automation executions across rules
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: All executions }

  /teams/{teamId}/automations/reorder:
    put:
      operationId: reorderAutomations
      summary: Reorder automation rules
      tags: [Automations]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items: { type: string }
      responses:
        '200': { description: Rules reordered }

  # ── Categories ──
  /teams/{teamId}/categories:
    get:
      operationId: listCategories
      summary: List article categories
      tags: [Categories]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: isPublic
          in: query
          schema: { type: boolean }
      responses:
        '200': { description: List of categories }
    post:
      operationId: createCategory
      summary: Create a category
      tags: [Categories]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                description: { type: string }
                isPublic: { type: boolean }
                sortOrder: { type: integer }
      responses:
        '201': { description: Category created }

  /teams/{teamId}/categories/{categoryId}:
    get:
      operationId: getCategory
      summary: Get a category
      tags: [Categories]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: categoryId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Category details }
    patch:
      operationId: updateCategory
      summary: Update a category
      tags: [Categories]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: categoryId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Updated category }
    delete:
      operationId: deleteCategory
      summary: Delete a category
      tags: [Categories]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: categoryId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Category deleted }

  # ── Community ──
  /teams/{teamId}/community/posts:
    get:
      operationId: listCommunityPosts
      summary: List community posts
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
        - name: topicId
          in: query
          schema: { type: string, format: uuid }
        - name: status
          in: query
          schema:
            type: string
            enum: [open, closed, pinned]
        - name: search
          in: query
          schema: { type: string }
      responses:
        '200': { description: List of posts }
    post:
      operationId: createCommunityPost
      summary: Create a community post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, body]
              properties:
                title: { type: string }
                body: { type: string }
                topicId: { type: string, format: uuid }
                attachments:
                  type: array
                  maxItems: 10
                  items: { $ref: '#/components/schemas/CommunityAttachment' }
                  description: Image attachments (max 10, images only, 5MB each)
      responses:
        '201': { description: Post created }

  /teams/{teamId}/community/posts/{postId}:
    get:
      operationId: getCommunityPost
      summary: Get a community post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Post details }
    patch:
      operationId: updateCommunityPost
      summary: Update a community post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title: { type: string }
                body: { type: string }
                status: { type: string, enum: [open, answered, officially_answered, planned, not_planned, completed, under_review] }
                isPinned: { type: boolean }
                isLocked: { type: boolean }
                attachments:
                  type: array
                  maxItems: 10
                  items: { $ref: '#/components/schemas/CommunityAttachment' }
      responses:
        '200': { description: Updated post }
    delete:
      operationId: deleteCommunityPost
      summary: Delete a community post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Post deleted }

  /teams/{teamId}/community/posts/{postId}/comments:
    get:
      operationId: listPostComments
      summary: List comments on a post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: List of comments }
    post:
      operationId: addPostComment
      summary: Add a comment to a post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [body]
              properties:
                body: { type: string }
                attachments:
                  type: array
                  maxItems: 5
                  items: { $ref: '#/components/schemas/CommunityAttachment' }
                  description: Image attachments (max 5, images only, 5MB each)
      responses:
        '201': { description: Comment added }

  /teams/{teamId}/community/posts/{postId}/vote:
    post:
      operationId: voteCommunityPost
      summary: Upvote or downvote a post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [direction]
              properties:
                direction:
                  type: string
                  enum: [up, down]
      responses:
        '200': { description: Vote recorded }

  /teams/{teamId}/community/posts/{postId}/follow:
    post:
      operationId: followCommunityPost
      summary: Follow or unfollow a post
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: postId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Follow toggled }

  /teams/{teamId}/community/topics:
    get:
      operationId: listCommunityTopics
      summary: List community topics
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of topics }

  /teams/{teamId}/community/topics/{topicId}:
    get:
      operationId: getCommunityTopic
      summary: Get a community topic
      tags: [Community]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: topicId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Topic details }

  # ── Mentions ──
  /teams/{teamId}/mentions:
    get:
      operationId: listMentions
      summary: List @mentions for a user
      tags: [Mentions]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string, format: uuid }
        - name: isRead
          in: query
          schema: { type: boolean }
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/pageSize'
      responses:
        '200': { description: List of mentions }

  /teams/{teamId}/mentions/{mentionId}/read:
    patch:
      operationId: markMentionRead
      summary: Mark a mention as read
      tags: [Mentions]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: mentionId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Mention marked read }

  /teams/{teamId}/mentions/unread-count:
    get:
      operationId: getUnreadMentionCount
      summary: Get unread mention count
      tags: [Mentions]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: userId
          in: query
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Unread count }

  /teams/{teamId}/mentions/mark-all-read:
    post:
      operationId: markAllMentionsRead
      summary: Mark all mentions as read
      tags: [Mentions]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userId]
              properties:
                userId: { type: string, format: uuid }
      responses:
        '200': { description: All mentions marked read }

  # ── API Keys ──
  /teams/{teamId}/keys:
    get:
      operationId: listApiKeys
      summary: List all API keys
      tags: [API Keys]
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200': { description: List of API keys }
    post:
      operationId: createApiKey
      summary: Create a new API key
      description: Returns the full key value only once. Store it securely.
      tags: [API Keys]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type]
              properties:
                name: { type: string }
                type:
                  type: string
                  enum: [secret, publishable]
      responses:
        '201': { description: API key created (includes full key) }

  /teams/{teamId}/keys/{keyId}:
    delete:
      operationId: deleteApiKey
      summary: Revoke an API key
      tags: [API Keys]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: keyId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Key revoked }

  # ── Import Log Detail ──
  /teams/{teamId}/import/logs/{logId}:
    get:
      operationId: getImportLog
      summary: Get a single import log with details
      tags: [Import]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: logId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Import log details }

  # ── Events (SSE) ──
  /teams/{teamId}/events/stream:
    get:
      operationId: streamEvents
      summary: Real-time event stream (SSE)
      description: |
        Server-Sent Events stream of team events. Supports filtering by type and replaying missed events.

        Event types: ticket.created, ticket.updated, ticket.closed, ticket.assigned,
        message.created, message.updated, notification.created, mention.created
      tags: [Events]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: types
          in: query
          description: Comma-separated event types to filter
          schema: { type: string }
        - name: since
          in: query
          description: ISO-8601 timestamp to replay missed events
          schema: { type: string, format: date-time }
      responses:
        '200':
          description: SSE event stream
          content:
            text/event-stream:
              schema:
                type: string

  # ── API Logs ──
  /teams/{teamId}/logs:
    get:
      operationId: listApiLogs
      summary: List recent API request logs
      tags: [Logs]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: limit
          in: query
          schema: { type: integer, default: 50 }
        - name: method
          in: query
          schema: { type: string, enum: [GET, POST, PATCH, PUT, DELETE] }
        - name: status
          in: query
          description: Filter by HTTP status code
          schema: { type: integer }
        - name: since
          in: query
          schema: { type: string, format: date-time }
        - name: tail
          in: query
          description: Stream new logs in real-time (SSE)
          schema: { type: boolean }
      responses:
        '200': { description: API request logs }

  # ── Webhook Listeners (CLI) ──
  /teams/{teamId}/webhooks/listen:
    post:
      operationId: registerWebhookListener
      summary: Register a CLI webhook listener
      description: Creates a temporary listener for CLI `cstar listen`. Returns a listenerId and signing secret.
      tags: [Webhooks]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                events:
                  type: array
                  items: { type: string }
                  description: Event types to listen for (empty = all)
      responses:
        '201':
          description: Listener registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: object
                    properties:
                      listenerId: { type: string, format: uuid }
                      secret: { type: string }
                      expiresAt: { type: string, format: date-time }

  /teams/{teamId}/webhooks/listen/{listenerId}:
    get:
      operationId: pollWebhookListener
      summary: Poll or stream events for a CLI listener
      tags: [Webhooks]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: listenerId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: SSE event stream or polled events
          content:
            text/event-stream:
              schema:
                type: string
    delete:
      operationId: removeWebhookListener
      summary: Remove a CLI webhook listener
      tags: [Webhooks]
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: listenerId
          in: path
          required: true
          schema: { type: string, format: uuid }
      responses:
        '200': { description: Listener removed }

  /teams/{teamId}/webhooks/trigger-test:
    post:
      operationId: triggerTestWebhook
      summary: Broadcast a test event to all active webhooks and CLI listeners
      tags: [Webhooks]
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  description: Event type to simulate
      responses:
        '200': { description: Test event broadcast result }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key in Bearer format

  parameters:
    teamId:
      name: teamId
      in: path
      required: true
      description: Team UUID
      schema:
        type: string
        format: uuid

    ticketId:
      name: ticketId
      in: path
      required: true
      description: Ticket UUID
      schema:
        type: string
        format: uuid

    customerId:
      name: customerId
      in: path
      required: true
      description: Customer UUID
      schema:
        type: string
        format: uuid

    articleId:
      name: articleId
      in: path
      required: true
      description: Article UUID
      schema:
        type: string
        format: uuid

    webhookId:
      name: webhookId
      in: path
      required: true
      description: Webhook UUID
      schema:
        type: string
        format: uuid

    page:
      name: page
      in: query
      description: Page number (starting from 1)
      schema:
        type: integer
        default: 1
        minimum: 1

    pageSize:
      name: pageSize
      in: query
      description: Items per page
      schema:
        type: integer
        default: 20
        minimum: 1
        maximum: 100

  schemas:
    CommunityAttachment:
      type: object
      description: Image attachment metadata (JPEG, PNG, GIF, WebP only, max 5MB)
      required: [filename, content_type, size_bytes, storage_path]
      properties:
        filename: { type: string, description: Original filename }
        content_type: { type: string, description: 'MIME type (image/jpeg, image/png, image/gif, image/webp)', enum: [image/jpeg, image/png, image/gif, image/webp] }
        size_bytes: { type: integer, description: File size in bytes (max 5242880) }
        storage_path: { type: string, description: Path in storage bucket }

    Meta:
      type: object
      properties:
        requestId:
          type: string
          description: Unique request identifier
        timestamp:
          type: string
          format: date-time
          description: Request timestamp

    Pagination:
      type: object
      properties:
        total:
          type: integer
          description: Total number of items
        page:
          type: integer
          description: Current page number
        pageSize:
          type: integer
          description: Items per page
        hasMore:
          type: boolean
          description: Whether more items exist

    Error:
      type: object
      properties:
        code:
          type: string
          description: Error code (e.g., parameter_missing, resource_not_found)
        message:
          type: string
          description: Human-readable error message
        field:
          type: string
          description: Field that caused the error (if applicable)
        docs:
          type: string
          description: Link to error documentation

    Ticket:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        priority:
          type: string
          enum: [low, normal, high, urgent]
        status:
          type: string
        customerId:
          type: string
          format: uuid
          nullable: true
        customerName:
          type: string
          nullable: true
        assignedTo:
          type: string
          format: uuid
          nullable: true
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
          nullable: true
        messageCount:
          type: integer
        responseDeadline:
          type: string
          format: date-time
          nullable: true
        resolutionDeadline:
          type: string
          format: date-time
          nullable: true
        firstResponseAt:
          type: string
          format: date-time
          nullable: true
        responseTier:
          type: string
          nullable: true
        resolutionTier:
          type: string
          nullable: true
        slaPaused:
          type: boolean
        createdAt:
          type: string
          format: date-time
        closedAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time

    Message:
      type: object
      properties:
        id:
          type: string
          format: uuid
        ticketId:
          type: string
          format: uuid
        sender:
          type: string
          enum: [customer, agent, system]
        content:
          type: string
        senderId:
          type: string
          format: uuid
          nullable: true
        senderName:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time

    Customer:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          format: email
        sentiment:
          type: string
        status:
          type: string
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
          nullable: true
        customFields:
          type: object
          additionalProperties: true
        ticketCount:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    Article:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        slug:
          type: string
        excerpt:
          type: string
          nullable: true
        content:
          type: string
          nullable: true
        category:
          type: string
        status:
          type: string
          enum: [draft, published, archived]
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
          nullable: true
        viewCount:
          type: integer
        useCount:
          type: integer
        readTime:
          type: string
        isPublic:
          type: boolean
        publishedAt:
          type: string
          format: date-time
          nullable: true
        metaDescription:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    CreateTicketRequest:
      type: object
      required:
        - title
      properties:
        title:
          type: string
          minLength: 1
        priority:
          type: string
          enum: [low, normal, high, urgent]
          default: normal
        status:
          type: string
          default: new
        customerId:
          type: string
          format: uuid
        customerName:
          type: string
        tags:
          type: array
          items:
            type: string
        notes:
          type: string

    UpdateTicketRequest:
      type: object
      properties:
        title:
          type: string
        priority:
          type: string
          enum: [low, normal, high, urgent]
        status:
          type: string
        customerId:
          type: string
          format: uuid
          nullable: true
        customerName:
          type: string
        assignedTo:
          type: string
          format: uuid
          nullable: true
        tags:
          type: array
          items:
            type: string
        notes:
          type: string

    CreateMessageRequest:
      type: object
      required:
        - content
      properties:
        content:
          type: string
          minLength: 1
        sender:
          type: string
          enum: [customer, agent, system]
          default: agent
        senderId:
          type: string
          format: uuid
        senderName:
          type: string

    CreateCustomerRequest:
      type: object
      required:
        - name
        - email
      properties:
        name:
          type: string
          minLength: 1
        email:
          type: string
          format: email
        sentiment:
          type: string
          default: neutral
        status:
          type: string
          default: active
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
        customFields:
          type: object
          additionalProperties: true

    UpdateCustomerRequest:
      type: object
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        sentiment:
          type: string
        status:
          type: string
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
        customFields:
          type: object
          additionalProperties: true

    CreateArticleRequest:
      type: object
      required:
        - title
      properties:
        title:
          type: string
          minLength: 1
        content:
          type: string
        excerpt:
          type: string
        category:
          type: string
          default: general
        status:
          type: string
          enum: [draft, published, archived]
          default: draft
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
        isPublic:
          type: boolean
          default: false
        slug:
          type: string
        metaDescription:
          type: string

    UpdateArticleRequest:
      type: object
      properties:
        title:
          type: string
        content:
          type: string
        excerpt:
          type: string
        category:
          type: string
        status:
          type: string
          enum: [draft, published, archived]
        tags:
          type: array
          items:
            type: string
        notes:
          type: string
        isPublic:
          type: boolean
        slug:
          type: string
        metaDescription:
          type: string

    TicketResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/Ticket'
        meta:
          $ref: '#/components/schemas/Meta'

    TicketDetailResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          allOf:
            - $ref: '#/components/schemas/Ticket'
            - type: object
              properties:
                messages:
                  type: array
                  items:
                    $ref: '#/components/schemas/Message'
        meta:
          $ref: '#/components/schemas/Meta'

    TicketListResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/Ticket'
        pagination:
          $ref: '#/components/schemas/Pagination'
        meta:
          $ref: '#/components/schemas/Meta'

    MessageResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/Message'
        meta:
          $ref: '#/components/schemas/Meta'

    MessageListResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/Message'
        meta:
          $ref: '#/components/schemas/Meta'

    CustomerResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/Customer'
        meta:
          $ref: '#/components/schemas/Meta'

    CustomerDetailResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          allOf:
            - $ref: '#/components/schemas/Customer'
            - type: object
              properties:
                recentTickets:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      title:
                        type: string
                      status:
                        type: string
                      priority:
                        type: string
                      createdAt:
                        type: string
                        format: date-time
        meta:
          $ref: '#/components/schemas/Meta'

    CustomerListResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/Customer'
        pagination:
          $ref: '#/components/schemas/Pagination'
        meta:
          $ref: '#/components/schemas/Meta'

    ArticleResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/Article'
        meta:
          $ref: '#/components/schemas/Meta'

    ArticleListResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/Article'
        pagination:
          $ref: '#/components/schemas/Pagination'
        meta:
          $ref: '#/components/schemas/Meta'

    DeleteResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          properties:
            deleted:
              type: boolean
              const: true
            id:
              type: string
              format: uuid
        meta:
          $ref: '#/components/schemas/Meta'

    # Webhook Schemas
    Webhook:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
          description: Event types this webhook subscribes to
        isActive:
          type: boolean
        includeFullObjects:
          type: boolean
          description: Include complete object data in payloads
        flatPayload:
          type: boolean
          description: Flatten nested fields for visual builders
        lastTriggeredAt:
          type: string
          format: date-time
          nullable: true
        lastSuccessAt:
          type: string
          format: date-time
          nullable: true
        lastFailureAt:
          type: string
          format: date-time
          nullable: true
        consecutiveFailures:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    CreateWebhookRequest:
      type: object
      required:
        - name
        - url
        - events
      properties:
        name:
          type: string
          minLength: 1
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
              - ticket.created
              - ticket.updated
              - ticket.closed
              - customer.created
              - customer.updated
              - article.created
              - article.published
              - article.updated
              - boss.spawned
              - boss.defeated
              - player.level_up
              - achievement.unlocked
              - survey.submitted
          minItems: 1
        includeFullObjects:
          type: boolean
          default: true
        flatPayload:
          type: boolean
          default: false

    UpdateWebhookRequest:
      type: object
      properties:
        name:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        isActive:
          type: boolean
        includeFullObjects:
          type: boolean
        flatPayload:
          type: boolean

    WebhookResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/Webhook'
        meta:
          $ref: '#/components/schemas/Meta'

    WebhookListResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: array
          items:
            $ref: '#/components/schemas/Webhook'
        meta:
          $ref: '#/components/schemas/Meta'

    WebhookCreatedResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          allOf:
            - $ref: '#/components/schemas/Webhook'
            - type: object
              properties:
                secret:
                  type: string
                  description: HMAC signing secret (only shown once!)
        meta:
          $ref: '#/components/schemas/Meta'

    WebhookTestResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          properties:
            deliveryId:
              type: string
              format: uuid
            status:
              type: string
              enum: [success, failed]
            responseStatus:
              type: integer
              description: HTTP status code from the endpoint
            responseTimeMs:
              type: integer
              description: Response time in milliseconds
            error:
              type: string
              nullable: true
        meta:
          $ref: '#/components/schemas/Meta'

  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                const: false
              error:
                $ref: '#/components/schemas/Error'
              meta:
                $ref: '#/components/schemas/Meta'

    Forbidden:
      description: API key lacks required permissions
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                const: false
              error:
                $ref: '#/components/schemas/Error'
              meta:
                $ref: '#/components/schemas/Meta'

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                const: false
              error:
                $ref: '#/components/schemas/Error'
              meta:
                $ref: '#/components/schemas/Meta'

    ValidationError:
      description: Invalid request body or parameters
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                const: false
              error:
                $ref: '#/components/schemas/Error'
              meta:
                $ref: '#/components/schemas/Meta'

    RateLimited:
      description: Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
          description: Maximum requests per window
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: Remaining requests in current window
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Unix timestamp when window resets
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
      content:
        application/json:
          schema:
            type: object
            properties:
              success:
                type: boolean
                const: false
              error:
                type: object
                properties:
                  code:
                    type: string
                    const: rate_limit_exceeded
                  message:
                    type: string
                  retryAfter:
                    type: integer
              meta:
                $ref: '#/components/schemas/Meta'
