openapi: 3.1.0
info:
  title: IdeaLift API
  version: 1.1.0
  description: |
    The IdeaLift REST API lets you programmatically manage product ideas,
    roadmaps, customers, analytics, and webhook subscriptions.

    ## Authentication

    All requests require a **Bearer** token. Create an API key on the
    [Integrations page](https://idealift.app/dashboard/integrations),
    then include it in every request:

    ```
    Authorization: Bearer il_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ```

    ### Scoped API Keys

    Each API key is assigned one or more **scopes** that control which
    endpoints it can access:

    | Scope | Grants access to |
    |-------|-----------------|
    | `ideas:read` | List / get ideas, decisions, signals, evidence, votes, meetings, support tickets, participants |
    | `ideas:write` | Create, update, delete ideas |
    | `ideas:vote` | Vote on ideas |
    | `webhooks:manage` | CRUD webhook subscriptions |
    | `workspace:read` | Read workspace info, tags, products, members |
    | `roadmap:read` | List roadmap stages |
    | `roadmap:write` | Assign / unassign ideas to roadmap stages |
    | `customers:read` | List / get customers and their linked ideas |
    | `customers:write` | Create, update customers; link customers to ideas |
    | `analytics:read` | Read analytics (summary, decisions, funnel, signals, roadmap, revenue, categories, time-series) |

    Calling an endpoint without the required scope returns a
    **403 insufficient_scope** error.

    ## Rate Limits

    Every response includes standard rate-limit headers:

    | Header | Description |
    |--------|-------------|
    | `RateLimit-Limit` | Maximum requests allowed in the window |
    | `RateLimit-Remaining` | Requests remaining in the current window |
    | `RateLimit-Reset` | Seconds until the window resets |

    When the limit is exceeded the API returns **429 rate_limited**.

    ### Rate limits by plan

    | Plan | Requests/minute |
    |------|----------------|
    | Starter (Free) | 60 |
    | Pro | 120 |
    | Growth | 300 |
    | Scale | 600 |
    | Enterprise | 1,200 |

    **Export endpoints** have a separate limit of **10 requests per hour** per workspace,
    shared across all export types (ideas, customers, decisions, signals).

    ## Workspace Isolation

    All API keys are scoped to a single workspace. Every database query is
    filtered by `workspaceId` — it is impossible to access data from another
    workspace via the API, even with a valid key.

    ## Pagination

    List endpoints use **cursor-based pagination**. Pass `limit` to control
    page size and use the `cursor` value from the previous response to
    fetch the next page.

    ## Errors

    All errors follow a consistent shape:

    ```json
    {
      "error": {
        "code": "not_found",
        "message": "Idea not found"
      }
    }
    ```

    Validation errors include an optional `field` property:

    ```json
    {
      "error": {
        "code": "validation_error",
        "message": "title must be between 1 and 500 characters",
        "field": "title"
      }
    }
    ```

    ### Error codes

    | Code | HTTP Status | Description |
    |------|-------------|-------------|
    | `invalid_json` | 400 | Request body is not valid JSON |
    | `validation_error` | 400 | A field failed validation |
    | `unauthorized` | 401 | Missing or malformed Authorization header |
    | `invalid_key` | 401 | API key is invalid or revoked |
    | `insufficient_scope` | 403 | API key lacks the required scope |
    | `plan_required` | 403 | Feature not available on current plan |
    | `not_found` | 404 | Resource does not exist or is not accessible |
    | `conflict` | 409 | Duplicate or conflicting resource |
    | `limit_exceeded` | 422 | A resource limit has been reached (e.g., max webhooks) |
    | `rate_limited` | 429 | Too many requests |
    | `internal_error` | 500 | Unexpected server error |

    ## Webhook Delivery

    When an event fires, IdeaLift sends a POST request to your webhook URL
    with the following headers:

    | Header | Description |
    |--------|-------------|
    | `X-IdeaLift-Signature` | HMAC-SHA256 hex digest of the raw body using your webhook secret |
    | `X-IdeaLift-Timestamp` | Unix timestamp (seconds) when the delivery was created |
    | `X-IdeaLift-Event` | Event type (e.g., `idea.created`) |
    | `X-IdeaLift-Delivery` | Unique delivery ID (UUID) |

    Verify the signature by computing `HMAC-SHA256(secret, timestamp + "." + body)`
    and comparing in constant time.
  contact:
    name: IdeaLift Support
    email: support@idealift.app
    url: https://idealift.app
  license:
    name: Proprietary
    url: https://idealift.app/terms

servers:
  - url: https://idealift.app
    description: Production

tags:
  - name: Ideas
    description: Create, read, update, and delete product ideas.
  - name: Idea Relationships
    description: Votes, signals, decisions, customers, evidence, meetings, support tickets, and participants linked to ideas.
  - name: Batch Operations
    description: Bulk create ideas and bulk update statuses.
  - name: Customers
    description: Create, read, and update customer records.
  - name: Webhooks
    description: Manage event-driven webhook subscriptions with delivery tracking.
  - name: Workspace
    description: Read workspace configuration, tags, products, and members.
  - name: Roadmap
    description: Roadmap stages and idea assignments.
  - name: Analytics
    description: Aggregated analytics — summary, decisions, funnel, signals, roadmap, revenue impact, categories, and time-series.
  - name: Export
    description: Bulk data export in CSV or JSON format (rate limited to 10/hour).

security:
  - bearerAuth: []

# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
paths:
  # ---- Ideas (collection) -------------------------------------------------
  /api/v1/ideas:
    get:
      operationId: listIdeas
      summary: List ideas
      description: |
        Returns a paginated list of ideas in the workspace. Supports
        filtering by status, source, category, destination, roadmap stage,
        date range, and free-text search.
      tags: [Ideas]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - name: sort
          in: query
          description: Field to sort results by.
          schema:
            type: string
            enum: [createdAt, voteCount, riceScore, validationScore, signalCount, lastActivityAt, totalCustomerARR, shippedAt, decidedAt]
            default: createdAt
        - name: order
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
        - name: status
          in: query
          schema:
            $ref: '#/components/schemas/IdeaStatus'
        - name: source
          in: query
          schema:
            $ref: '#/components/schemas/IdeaSource'
        - name: category
          in: query
          schema:
            type: string
            maxLength: 100
        - name: destination
          in: query
          schema:
            type: string
        - name: roadmapStageId
          in: query
          schema:
            type: string
            format: uuid
        - name: since
          in: query
          description: Return only ideas created at or after this timestamp (ISO 8601).
          schema:
            type: string
            format: date-time
        - name: search
          in: query
          schema:
            type: string
            maxLength: 200
        - name: updated_since
          in: query
          description: Ideas with activity since this date.
          schema:
            type: string
            format: date-time
        - name: decided_since
          in: query
          description: Ideas decided since this date.
          schema:
            type: string
            format: date-time
        - name: shipped_since
          in: query
          description: Ideas shipped since this date.
          schema:
            type: string
            format: date-time
        - name: product_id
          in: query
          schema:
            type: string
            format: uuid
        - name: tag_id
          in: query
          schema:
            type: string
            format: uuid
        - name: decision_owner
          in: query
          description: Filter by decision owner email.
          schema:
            type: string
        - name: has_customers
          in: query
          description: Filter to ideas that have linked customers.
          schema:
            type: boolean
        - name: min_arr
          in: query
          description: Minimum total customer ARR.
          schema:
            type: number
      responses:
        '200':
          description: A paginated list of ideas.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Idea'
                  pagination:
                    $ref: '#/components/schemas/PaginationWithTotal'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '429':
          $ref: '#/components/responses/RateLimited'

    post:
      operationId: createIdea
      summary: Create an idea
      tags: [Ideas]
      security:
        - bearerAuth: [ideas:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIdeaRequest'
      responses:
        '200':
          description: Idea created successfully.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/Idea'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '429':
          $ref: '#/components/responses/RateLimited'

  # ---- Ideas (single) -----------------------------------------------------
  /api/v1/ideas/{id}:
    parameters:
      - $ref: '#/components/parameters/IdeaId'

    get:
      operationId: getIdea
      summary: Get an idea
      description: |
        Returns full detail including tags, external work item context,
        RICE/validation scores, decision ownership, and source context.
      tags: [Ideas]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Idea detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/IdeaDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateIdea
      summary: Update an idea
      tags: [Ideas]
      security:
        - bearerAuth: [ideas:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateIdeaRequest'
      responses:
        '200':
          description: Updated idea detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/IdeaDetail'
        '400':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'

    delete:
      operationId: deleteIdea
      summary: Delete an idea
      description: Soft-deletes the idea (sets status to `deleted`).
      tags: [Ideas]
      security:
        - bearerAuth: [ideas:write]
      responses:
        '200':
          description: Idea deleted.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/DeleteResult'
        '404':
          $ref: '#/components/responses/NotFound'

  # ---- Idea vote -----------------------------------------------------------
  /api/v1/ideas/{id}/vote:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    post:
      operationId: voteOnIdea
      summary: Vote on an idea
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:vote]
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VoteRequest'
      responses:
        '200':
          description: Vote recorded.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/VoteResult'

  # ---- Idea votes ----------------------------------------------------------
  /api/v1/ideas/{id}/votes:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: getIdeaVotes
      summary: Get idea vote breakdown
      description: Returns internal vote history, public vote total, and request count.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Vote breakdown.

  # ---- Idea signals --------------------------------------------------------
  /api/v1/ideas/{id}/signals:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaSignals
      summary: List idea signals
      description: Signal history with platform, user, sentiment, and similarity scores.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Paginated signal list.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Signal'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  # ---- Idea decisions ------------------------------------------------------
  /api/v1/ideas/{id}/decisions:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaDecisions
      summary: List idea decisions
      description: Decision event history including status changes, closures, and snooze events.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Paginated decision list.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Decision'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  # ---- Idea customers ------------------------------------------------------
  /api/v1/ideas/{id}/customers:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaCustomers
      summary: List customers linked to an idea
      tags: [Idea Relationships]
      security:
        - bearerAuth: [customers:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Paginated customer list.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Customer'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
    post:
      operationId: linkCustomerToIdea
      summary: Link a customer to an idea
      tags: [Idea Relationships]
      security:
        - bearerAuth: [customers:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkCustomerRequest'
      responses:
        '200':
          description: Customer linked.

  # ---- Idea evidence -------------------------------------------------------
  /api/v1/ideas/{id}/evidence:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaEvidence
      summary: List idea evidence artifacts
      description: Returns evidence items (documents, screenshots, Jira issues, etc.) linked to the idea.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Evidence list.

  # ---- Idea participants ---------------------------------------------------
  /api/v1/ideas/{id}/participants:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaParticipants
      summary: List decision participants
      description: Returns approvers/reviewers involved in the idea's decision process.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Participant list.

  # ---- Idea meetings -------------------------------------------------------
  /api/v1/ideas/{id}/meetings:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaMeetings
      summary: List linked meeting transcripts
      description: Returns meeting transcripts where this idea was mentioned or extracted.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Meeting transcript list.

  # ---- Idea support tickets ------------------------------------------------
  /api/v1/ideas/{id}/support-tickets:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    get:
      operationId: listIdeaSupportTickets
      summary: List linked support tickets
      description: Returns support tickets (Zendesk, Intercom, Freshdesk, etc.) linked to the idea.
      tags: [Idea Relationships]
      security:
        - bearerAuth: [ideas:read]
      responses:
        '200':
          description: Support ticket list.

  # ---- Idea roadmap assignment ---------------------------------------------
  /api/v1/ideas/{id}/roadmap:
    parameters:
      - $ref: '#/components/parameters/IdeaId'
    put:
      operationId: assignIdeaToRoadmap
      summary: Assign idea to roadmap stage
      tags: [Roadmap]
      security:
        - bearerAuth: [roadmap:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RoadmapAssignRequest'
      responses:
        '200':
          description: Roadmap assignment updated.

  # ---- Batch operations ----------------------------------------------------
  /api/v1/ideas/batch:
    post:
      operationId: batchCreateIdeas
      summary: Batch create ideas
      description: Creates up to 100 ideas in a single transaction.
      tags: [Batch Operations]
      security:
        - bearerAuth: [ideas:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchCreateRequest'
      responses:
        '200':
          description: Batch result.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/BatchCreateResult'

  /api/v1/ideas/batch/status:
    patch:
      operationId: batchUpdateStatus
      summary: Batch update idea statuses
      description: Updates the status of up to 100 ideas in a single transaction.
      tags: [Batch Operations]
      security:
        - bearerAuth: [ideas:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchStatusUpdateRequest'
      responses:
        '200':
          description: Batch result.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/BatchStatusResult'

  # ---- Customers -----------------------------------------------------------
  /api/v1/customers:
    get:
      operationId: listCustomers
      summary: List customers
      description: |
        Returns a paginated list of customers in the workspace. Supports
        search, ARR filtering, and sort by name, ARR, or creation date.
      tags: [Customers]
      security:
        - bearerAuth: [customers:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - name: sort
          in: query
          schema:
            type: string
            enum: [createdAt, arr, name]
            default: createdAt
        - name: order
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
        - name: search
          in: query
          description: Search by name, email, or domain.
          schema:
            type: string
            maxLength: 200
        - name: min_arr
          in: query
          schema:
            type: number
        - name: plan
          in: query
          schema:
            type: string
        - name: source
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Paginated customer list.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CustomerFull'
                  pagination:
                    $ref: '#/components/schemas/PaginationWithTotal'

    post:
      operationId: createCustomer
      summary: Create a customer
      tags: [Customers]
      security:
        - bearerAuth: [customers:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomerRequest'
      responses:
        '200':
          description: Customer created.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/CustomerFull'
        '400':
          $ref: '#/components/responses/ValidationError'

  /api/v1/customers/{id}:
    parameters:
      - $ref: '#/components/parameters/CustomerId'
    get:
      operationId: getCustomer
      summary: Get a customer
      tags: [Customers]
      security:
        - bearerAuth: [customers:read]
      responses:
        '200':
          description: Customer detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/CustomerFull'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      operationId: updateCustomer
      summary: Update a customer
      description: Updates one or more fields. Omitted fields are left unchanged.
      tags: [Customers]
      security:
        - bearerAuth: [customers:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomerRequest'
      responses:
        '200':
          description: Updated customer.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/CustomerFull'
        '400':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/v1/customers/{id}/ideas:
    parameters:
      - $ref: '#/components/parameters/CustomerId'
    get:
      operationId: listCustomerIdeas
      summary: List ideas linked to a customer
      tags: [Customers]
      security:
        - bearerAuth: [customers:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Paginated idea list.
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Idea'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  # ---- Tags ----------------------------------------------------------------
  /api/v1/tags:
    get:
      operationId: listTags
      summary: List workspace tags
      description: Returns all tags with idea counts.
      tags: [Workspace]
      security:
        - bearerAuth: [workspace:read]
      responses:
        '200':
          description: Tag list.

  # ---- Products ------------------------------------------------------------
  /api/v1/products:
    get:
      operationId: listProducts
      summary: List workspace products
      description: Returns all products with idea counts and parent relationships.
      tags: [Workspace]
      security:
        - bearerAuth: [workspace:read]
      responses:
        '200':
          description: Product list.

  # ---- Members -------------------------------------------------------------
  /api/v1/members:
    get:
      operationId: listMembers
      summary: List workspace members
      description: Returns team members with roles and dashboard access info.
      tags: [Workspace]
      security:
        - bearerAuth: [workspace:read]
      responses:
        '200':
          description: Member list.

  # ---- Webhooks ------------------------------------------------------------
  /api/v1/webhooks:
    get:
      operationId: listWebhooks
      summary: List webhook subscriptions
      tags: [Webhooks]
      security:
        - bearerAuth: [webhooks:manage]
      responses:
        '200':
          description: Webhook subscription list.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookSubscription'

    post:
      operationId: createWebhook
      summary: Create a webhook subscription
      description: |
        Registers a new webhook endpoint. Maximum 10 subscriptions per
        workspace. The signing `secret` is returned **only at creation time**.
      tags: [Webhooks]
      security:
        - bearerAuth: [webhooks:manage]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '200':
          description: Webhook created (includes full secret).
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/WebhookSubscription'

  /api/v1/webhooks/{id}:
    parameters:
      - $ref: '#/components/parameters/WebhookId'
    get:
      operationId: getWebhook
      summary: Get webhook detail
      description: Returns webhook subscription with up to 20 recent delivery attempts.
      tags: [Webhooks]
      security:
        - bearerAuth: [webhooks:manage]
      responses:
        '200':
          description: Webhook detail with deliveries.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/WebhookSubscriptionDetail'

    delete:
      operationId: deleteWebhook
      summary: Delete a webhook subscription
      tags: [Webhooks]
      security:
        - bearerAuth: [webhooks:manage]
      responses:
        '200':
          description: Webhook deleted.

  # ---- Workspace -----------------------------------------------------------
  /api/v1/workspace:
    get:
      operationId: getWorkspace
      summary: Get workspace info
      tags: [Workspace]
      security:
        - bearerAuth: [workspace:read]
      responses:
        '200':
          description: Workspace info.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/WorkspaceInfo'

  # ---- Roadmap stages ------------------------------------------------------
  /api/v1/roadmap/stages:
    get:
      operationId: listRoadmapStages
      summary: List roadmap stages
      tags: [Roadmap]
      security:
        - bearerAuth: [roadmap:read]
      responses:
        '200':
          description: Roadmap stage list.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/RoadmapStage'

  # ---- Analytics -----------------------------------------------------------
  /api/v1/analytics/summary:
    get:
      operationId: getAnalyticsSummary
      summary: Get analytics summary
      description: |
        Returns aggregate analytics including idea counts, status/source
        breakdowns, all categories with counts, average RICE scores,
        customer count, and revenue summary.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      responses:
        '200':
          description: Analytics summary.

  /api/v1/analytics/ideas-over-time:
    get:
      operationId: getIdeasOverTime
      summary: Ideas over time
      description: |
        Time-series idea counts bucketed by day, week, or month. Optional
        `group_by` parameter to break down by status, source, category, or product.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: week
        - name: group_by
          in: query
          schema:
            type: string
            enum: [status, source, category, product]
        - $ref: '#/components/parameters/StartDate'
        - $ref: '#/components/parameters/EndDate'
      responses:
        '200':
          description: Time-series data.

  /api/v1/analytics/decisions:
    get:
      operationId: getDecisionAnalytics
      summary: Decision metrics
      description: |
        Decision summary (total, avg days to decision, by outcome),
        breakdown by decision owner (with overdue tracking), and
        decisions over time.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
        - $ref: '#/components/parameters/StartDate'
        - $ref: '#/components/parameters/EndDate'
      responses:
        '200':
          description: Decision analytics.

  /api/v1/analytics/funnel:
    get:
      operationId: getFunnelAnalytics
      summary: Status conversion funnel
      description: |
        Idea counts per status with ARR and RICE averages. Includes
        timing metrics (avg days captured→decided, captured→shipped,
        decided→shipped).
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/StartDate'
        - $ref: '#/components/parameters/EndDate'
      responses:
        '200':
          description: Funnel metrics.

  /api/v1/analytics/signals:
    get:
      operationId: getSignalAnalytics
      summary: Signal trends
      description: |
        Signal volume over time with sentiment, breakdown by platform
        (unique ideas per platform), and breakdown by sentiment label.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: week
        - $ref: '#/components/parameters/StartDate'
        - $ref: '#/components/parameters/EndDate'
      responses:
        '200':
          description: Signal analytics.

  /api/v1/analytics/roadmap:
    get:
      operationId: getRoadmapAnalytics
      summary: Roadmap stage metrics
      description: |
        Ideas per roadmap stage with ARR, RICE averages, and shipping
        velocity (shipped in last 30/90 days). Includes unstaged idea count.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      responses:
        '200':
          description: Roadmap analytics.

  /api/v1/analytics/revenue-impact:
    get:
      operationId: getRevenueImpact
      summary: Revenue impact analysis
      description: |
        ARR breakdown by idea status and top 20 ideas ranked by total
        customer ARR with customer counts.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      responses:
        '200':
          description: Revenue analytics.

  /api/v1/analytics/categories:
    get:
      operationId: getCategoryAnalytics
      summary: Category breakdown
      description: |
        Full category breakdown with idea count, votes, signals, RICE
        average, ARR, decided/shipped counts, avg days to decision,
        and unique source count.
      tags: [Analytics]
      security:
        - bearerAuth: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/StartDate'
        - $ref: '#/components/parameters/EndDate'
      responses:
        '200':
          description: Category analytics.

  # ---- Export --------------------------------------------------------------
  /api/v1/export/ideas:
    get:
      operationId: exportIdeas
      summary: Bulk export ideas
      description: |
        Exports up to 10,000 ideas in CSV or JSON format. Supports filters
        and optional tag/customer enrichment. Rate limited to 10 exports
        per hour per workspace.
      tags: [Export]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - name: format
          in: query
          required: true
          schema:
            type: string
            enum: [csv, json]
            default: json
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 10000
            default: 10000
        - name: status
          in: query
          schema:
            $ref: '#/components/schemas/IdeaStatus'
        - name: category
          in: query
          schema:
            type: string
        - name: source
          in: query
          schema:
            type: string
        - name: start
          in: query
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          schema:
            type: string
            format: date-time
        - name: include_tags
          in: query
          schema:
            type: boolean
            default: false
        - name: include_customer_count
          in: query
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: CSV file or JSON array of ideas.
        '429':
          $ref: '#/components/responses/RateLimited'

  /api/v1/export/customers:
    get:
      operationId: exportCustomers
      summary: Bulk export customers
      description: Exports up to 10,000 customers in CSV or JSON format.
      tags: [Export]
      security:
        - bearerAuth: [customers:read]
      parameters:
        - name: format
          in: query
          schema:
            type: string
            enum: [csv, json]
            default: json
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 10000
      responses:
        '200':
          description: CSV file or JSON array of customers.
        '429':
          $ref: '#/components/responses/RateLimited'

  /api/v1/export/decisions:
    get:
      operationId: exportDecisions
      summary: Bulk export decision events
      description: Exports up to 10,000 decided ideas with decision context in CSV or JSON.
      tags: [Export]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - name: format
          in: query
          schema:
            type: string
            enum: [csv, json]
            default: json
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 10000
        - name: start
          in: query
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: CSV file or JSON array of decisions.
        '429':
          $ref: '#/components/responses/RateLimited'

  /api/v1/export/signals:
    get:
      operationId: exportSignals
      summary: Bulk export signal history
      description: Exports up to 10,000 signals with idea context in CSV or JSON.
      tags: [Export]
      security:
        - bearerAuth: [ideas:read]
      parameters:
        - name: format
          in: query
          schema:
            type: string
            enum: [csv, json]
            default: json
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 10000
        - name: start
          in: query
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          schema:
            type: string
            format: date-time
        - name: platform
          in: query
          schema:
            type: string
      responses:
        '200':
          description: CSV file or JSON array of signals.
        '429':
          $ref: '#/components/responses/RateLimited'

# ---------------------------------------------------------------------------
# Components
# ---------------------------------------------------------------------------
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: "il_live_*"
      description: |
        API key created in the IdeaLift dashboard. Keys start with
        `il_live_` (production) or `il_test_` (staging).

  # ---- Parameters ----------------------------------------------------------
  parameters:
    IdeaId:
      name: id
      in: path
      required: true
      description: Idea UUID.
      schema:
        type: string
        format: uuid

    CustomerId:
      name: id
      in: path
      required: true
      description: Customer UUID.
      schema:
        type: string
        format: uuid

    WebhookId:
      name: id
      in: path
      required: true
      description: Webhook subscription UUID.
      schema:
        type: string
        format: uuid

    Limit:
      name: limit
      in: query
      description: Maximum number of items to return.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25

    Cursor:
      name: cursor
      in: query
      description: Opaque cursor returned by a previous response for pagination.
      schema:
        type: string

    StartDate:
      name: start
      in: query
      description: Start of date range (ISO 8601). Defaults to 90 days ago.
      schema:
        type: string
        format: date-time

    EndDate:
      name: end
      in: query
      description: End of date range (ISO 8601). Defaults to now.
      schema:
        type: string
        format: date-time

  # ---- Headers -------------------------------------------------------------
  headers:
    RateLimit-Limit:
      description: Maximum requests allowed in the current window.
      schema:
        type: integer
    RateLimit-Remaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
    RateLimit-Reset:
      description: Seconds until the rate-limit window resets.
      schema:
        type: integer

  # ---- Schemas -------------------------------------------------------------
  schemas:
    # -- Enums ---------------------------------------------------------------
    IdeaStatus:
      type: string
      enum:
        - captured
        - new
        - under_review
        - planned
        - in_progress
        - shipped
        - rejected
        - archived
        - duplicate
        - deleted

    IdeaSource:
      type: string
      enum:
        - discord
        - slack
        - extension
        - sentry
        - teams
        - zapier
        - api
        - chrome
        - vscode
        - outlook
        - intercom
        - zendesk
        - freshdesk
        - helpscout
        - email

    WebhookEventType:
      type: string
      enum:
        - idea.created
        - idea.updated
        - idea.status_changed
        - idea.voted
        - idea.scored
        - idea.assigned
        - idea.deleted
        - idea.destination_created
        - idea.signal_detected
        - customer.created
        - customer.updated

    # -- RICE sub-object -----------------------------------------------------
    RiceScores:
      type: object
      properties:
        reach:
          type: number
          nullable: true
        impact:
          type: number
          nullable: true
        confidence:
          type: number
          nullable: true
        effort:
          type: number
          nullable: true
        score:
          type: number
          nullable: true
        priority:
          type: string
          nullable: true
          description: RICE priority label.
        source:
          type: string
          nullable: true
          description: How the RICE score was calculated (e.g., `manual`, `ai`).

    # -- Validation sub-object -----------------------------------------------
    ValidationScores:
      type: object
      properties:
        score:
          type: number
          nullable: true
          description: Aggregate validation score (0 - 100).
        evidenceCount:
          type: number
          nullable: true

    # -- Idea ----------------------------------------------------------------
    Idea:
      type: object
      required:
        - id
        - title
        - status
        - source
        - voteCount
        - signalCount
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        ideaLiftId:
          type: string
          description: Human-readable ID (e.g., `IDEA-42`).
        title:
          type: string
        summary:
          type: string
          nullable: true
        status:
          $ref: '#/components/schemas/IdeaStatus'
        source:
          type: string
        category:
          type: string
          nullable: true
        ideaType:
          type: string
          nullable: true
        url:
          type: string
          nullable: true
        destination:
          type: string
          nullable: true
        destinationIssueId:
          type: string
          nullable: true
        destinationIssueUrl:
          type: string
          nullable: true
        voteCount:
          type: integer
        publicVoteCount:
          type: integer
          description: Votes from public submission portal.
        requestCount:
          type: integer
          description: Number of feature requests.
        signalCount:
          type: integer
        rice:
          $ref: '#/components/schemas/RiceScores'
        validation:
          $ref: '#/components/schemas/ValidationScores'
        decisionReason:
          type: string
          nullable: true
        decidedAt:
          type: string
          format: date-time
          nullable: true
        closureCategory:
          type: string
          nullable: true
          description: Category assigned when decided (e.g., `shipped`, `rejected_scope`, `deferred_priority`).
        decisionOwnerName:
          type: string
          nullable: true
        decisionDeadline:
          type: string
          format: date-time
          nullable: true
        totalCustomerARR:
          type: number
          nullable: true
          description: Total ARR of all linked customers.
        lastActivityAt:
          type: string
          format: date-time
          nullable: true
        lastSignalAt:
          type: string
          format: date-time
          nullable: true
        uniqueSourceCount:
          type: integer
          description: Number of unique platforms this idea was mentioned on.
        averageSentiment:
          type: number
          nullable: true
          description: Average sentiment score (-1.0 to 1.0).
        workItemStatus:
          type: string
          nullable: true
          description: Status from linked external work item (Jira/Linear).
        shippedAt:
          type: string
          format: date-time
          nullable: true
        productId:
          type: string
          format: uuid
          nullable: true
        actionable:
          type: boolean
          description: Whether the idea is actionable.
        createdAt:
          type: string
          format: date-time
        roadmapStageId:
          type: string
          format: uuid
          nullable: true

    # -- IdeaDetail (extends Idea) -------------------------------------------
    IdeaDetail:
      description: Full idea with tags, external work item, and source context.
      allOf:
        - $ref: '#/components/schemas/Idea'
        - type: object
          properties:
            tags:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  color:
                    type: string
                    nullable: true
            externalWorkItem:
              type: object
              nullable: true
              properties:
                system:
                  type: string
                issueId:
                  type: string
                issueUrl:
                  type: string
                  nullable: true
                status:
                  type: string
                  nullable: true
            sourceUserId:
              type: string
              nullable: true
            sourceUserName:
              type: string
              nullable: true
            rawInput:
              type: string
              nullable: true

    # -- Customer (linked to idea) -------------------------------------------
    Customer:
      type: object
      required: [id, name, source, linkedAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          nullable: true
        domain:
          type: string
          nullable: true
        arr:
          type: number
          nullable: true
        mrr:
          type: number
          nullable: true
        plan:
          type: string
          nullable: true
        source:
          type: string
        linkedAt:
          type: string
          format: date-time

    # -- CustomerFull (standalone customer) ----------------------------------
    CustomerFull:
      type: object
      required: [id, name, source, createdAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          nullable: true
        domain:
          type: string
          nullable: true
        arr:
          type: number
          nullable: true
        mrr:
          type: number
          nullable: true
        plan:
          type: string
          nullable: true
        source:
          type: string
        externalId:
          type: string
          nullable: true
        hubSpotCompanyId:
          type: string
          nullable: true
        ideaCount:
          type: integer
          description: Number of ideas linked to this customer.
        metadata:
          type: object
          nullable: true
          description: Arbitrary JSON metadata.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
          nullable: true

    # -- Decision ------------------------------------------------------------
    Decision:
      type: object
      required: [id, eventType, source, createdAt]
      properties:
        id:
          type: string
          format: uuid
        eventType:
          type: string
        closureCategory:
          type: string
          nullable: true
        reason:
          type: string
          nullable: true
        confidenceScore:
          type: number
          nullable: true
        previousStatus:
          type: string
          nullable: true
        newStatus:
          type: string
          nullable: true
        actorEmail:
          type: string
          nullable: true
        actorName:
          type: string
          nullable: true
        source:
          type: string
        relatedIdeaId:
          type: string
          nullable: true
        externalIssueId:
          type: string
          nullable: true
        externalIssueUrl:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true
        createdAt:
          type: string
          format: date-time

    # -- Signal --------------------------------------------------------------
    Signal:
      type: object
      required: [id, platform, similarity, detectedAt]
      properties:
        id:
          type: string
          format: uuid
        platform:
          type: string
        userId:
          type: string
          nullable: true
        userName:
          type: string
          nullable: true
        userEmail:
          type: string
          nullable: true
        content:
          type: string
          nullable: true
        similarity:
          type: number
          description: Cosine similarity score (0 - 1).
        sentiment:
          type: string
          nullable: true
        sentimentScore:
          type: number
          nullable: true
        autoLinkedCustomerId:
          type: string
          nullable: true
        detectedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time

    # -- Webhook subscription ------------------------------------------------
    WebhookSubscription:
      type: object
      required: [id, targetUrl, events, secret, isActive, failureCount, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        targetUrl:
          type: string
        events:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
        secret:
          type: string
          description: Returned in full only on creation; masked thereafter.
        isActive:
          type: boolean
        failureCount:
          type: integer
        lastDeliveredAt:
          type: string
          format: date-time
          nullable: true
        lastFailedAt:
          type: string
          format: date-time
          nullable: true
        lastError:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    WebhookSubscriptionDetail:
      allOf:
        - $ref: '#/components/schemas/WebhookSubscription'
        - type: object
          properties:
            recentDeliveries:
              type: array
              maxItems: 20
              items:
                $ref: '#/components/schemas/WebhookDelivery'

    WebhookDelivery:
      type: object
      required: [id, eventId, eventType, status, attemptCount, createdAt]
      properties:
        id:
          type: string
          format: uuid
        eventId:
          type: string
        eventType:
          $ref: '#/components/schemas/WebhookEventType'
        status:
          type: string
          enum: [pending, delivered, failed]
        statusCode:
          type: integer
          nullable: true
        attemptCount:
          type: integer
        error:
          type: string
          nullable: true
        durationMs:
          type: integer
          nullable: true
        deliveredAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time

    # -- Workspace -----------------------------------------------------------
    WorkspaceInfo:
      type: object
      required: [id, name, plan, ideasTotal, ideasThisMonth, activeIdeas, publicSubmissionEnabled]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        shortCode:
          type: string
          nullable: true
        plan:
          type: string
        ideasTotal:
          type: integer
        ideasThisMonth:
          type: integer
        activeIdeas:
          type: integer
        defaultDestination:
          type: string
          nullable: true
        inboxMode:
          type: string
          nullable: true
        publicSubmissionEnabled:
          type: boolean
        slug:
          type: string
          nullable: true

    # -- Roadmap stage -------------------------------------------------------
    RoadmapStage:
      type: object
      required: [id, name, slug, position, isDefault, ideaCount, createdAt]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        color:
          type: string
          nullable: true
        position:
          type: integer
        isDefault:
          type: boolean
        ideaCount:
          type: integer
        createdAt:
          type: string
          format: date-time

    # -- Request bodies ------------------------------------------------------
    CreateIdeaRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 500
        summary:
          type: string
          maxLength: 5000
        source:
          type: string
          default: api
        category:
          type: string
          maxLength: 100
        url:
          type: string
          maxLength: 2000

    UpdateIdeaRequest:
      type: object
      minProperties: 1
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 500
        summary:
          type: string
          maxLength: 5000
        status:
          $ref: '#/components/schemas/IdeaStatus'
        category:
          type: string
          maxLength: 100
        decisionReason:
          type: string
          maxLength: 5000
        riceReach:
          type: number
        riceImpact:
          type: number
        riceConfidence:
          type: number
        riceEffort:
          type: number

    CreateCustomerRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
          maxLength: 255
        email:
          type: string
          nullable: true
        domain:
          type: string
          nullable: true
        arr:
          type: number
          nullable: true
          description: Annual recurring revenue.
        mrr:
          type: number
          nullable: true
          description: Monthly recurring revenue.
        plan:
          type: string
          nullable: true
        externalId:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true

    UpdateCustomerRequest:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
          maxLength: 255
        email:
          type: string
          nullable: true
        domain:
          type: string
          nullable: true
        arr:
          type: number
          nullable: true
        mrr:
          type: number
          nullable: true
        plan:
          type: string
          nullable: true
        externalId:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true

    VoteRequest:
      type: object
      properties:
        voter:
          type: string
          maxLength: 255
        value:
          type: integer
          enum: [-1, 1]
          default: 1

    RoadmapAssignRequest:
      type: object
      required: [stageId]
      properties:
        stageId:
          type: string
          format: uuid
          nullable: true

    LinkCustomerRequest:
      type: object
      required: [customerId]
      properties:
        customerId:
          type: string
          format: uuid

    CreateWebhookRequest:
      type: object
      required: [url, events]
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/WebhookEventType'
        name:
          type: string
          maxLength: 100
        secret:
          type: string
          minLength: 16

    BatchCreateRequest:
      type: object
      required: [ideas]
      properties:
        ideas:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/CreateIdeaRequest'

    BatchStatusUpdateRequest:
      type: object
      required: [updates]
      properties:
        updates:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            required: [id, status]
            properties:
              id:
                type: string
                format: uuid
              status:
                $ref: '#/components/schemas/IdeaStatus'

    # -- Response payloads ---------------------------------------------------
    VoteResult:
      type: object
      required: [ideaId, value, newVoteCount]
      properties:
        ideaId:
          type: string
          format: uuid
        voter:
          type: string
          nullable: true
        value:
          type: integer
        newVoteCount:
          type: integer

    DeleteResult:
      type: object
      required: [id, deleted]
      properties:
        id:
          type: string
        deleted:
          type: boolean
          const: true

    BatchCreateResult:
      type: object
      required: [total, succeeded, failed, results]
      properties:
        total:
          type: integer
        succeeded:
          type: integer
        failed:
          type: integer
        results:
          type: array
          items:
            type: object
            required: [index, success]
            properties:
              index:
                type: integer
              success:
                type: boolean
              id:
                type: string
                format: uuid
              error:
                $ref: '#/components/schemas/ErrorBody'

    BatchStatusResult:
      type: object
      required: [total, succeeded, failed, results]
      properties:
        total:
          type: integer
        succeeded:
          type: integer
        failed:
          type: integer
        results:
          type: array
          items:
            type: object
            required: [id, success]
            properties:
              id:
                type: string
                format: uuid
              success:
                type: boolean
              status:
                type: string
              error:
                $ref: '#/components/schemas/ErrorBody'

    # -- Pagination ----------------------------------------------------------
    Pagination:
      type: object
      required: [hasMore]
      properties:
        hasMore:
          type: boolean
        cursor:
          type: string
          nullable: true

    PaginationWithTotal:
      allOf:
        - $ref: '#/components/schemas/Pagination'
        - type: object
          required: [total]
          properties:
            total:
              type: integer

    # -- Error ---------------------------------------------------------------
    Error:
      type: object
      required: [error]
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'

    ErrorBody:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          enum:
            - invalid_json
            - validation_error
            - not_found
            - conflict
            - unauthorized
            - invalid_key
            - insufficient_scope
            - plan_required
            - limit_exceeded
            - rate_limited
            - internal_error
        message:
          type: string
        field:
          type: string

  # ---- Reusable responses --------------------------------------------------
  responses:
    ValidationError:
      description: Request body failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    InsufficientScope:
      description: API key lacks the required scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    RateLimited:
      description: Rate limit exceeded.
      headers:
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimit-Limit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimit-Remaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
