openapi: 3.1.0
info:
  title: Uptrace JSON API
  version: "1.0.0"
  description: |
    REST API for managing Uptrace resources including monitors and dashboards.
    This API allows you to create, update, retrieve, and delete monitors and dashboards programmatically.

    ## Authentication

    To use the API, you need a project ID (`<project_id>`) and a user authentication token (`<user_token>`).

    You can create an authentication token on your user profile page. The token has the same permissions as the user account, allowing access to the same resources.

    ### SSO

    User auth tokens do not work with Single Sign-On (SSO) because SSO requires Uptrace to redirect users to the SSO provider, which is not viable for the JSON API.

    If your organization uses SSO, you need to create a separate user account and statically grant it access to projects without relying on SSO authentication.

    ## Usage Limits

    Uptrace Cloud provides this API free of charge for occasional use to read small fractions of ingested data. For example, a few requests per hour to read data from the last few hours is acceptable.

    If you need higher usage limits, please contact [support](mailto:support@uptrace.dev) to discuss your use case.
  contact:
    name: Uptrace
    url: https://uptrace.dev
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://api.uptrace.dev
    description: Public API server
security:
  - bearerAuth: []
  - authTokenQuery: []

paths:
  /api/v1/annotations:
    post:
      summary: Create annotation
      operationId: create_annotation
      description: Create a chart annotation.
      tags: [Annotations]
      security:
        - uptraceDSNHeader: []
        - dsnQuery: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnnotationCreateRequest"
            example:
              name: Deployed to production
              fingerprint: v1.2.3
              attrs:
                service.version: v1.2.3
      responses:
        "200":
          description: Annotation created.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
  /api/v1/tracing/{project_id}/spans:
    get:
      summary: List spans
      x-badges:
        - name: global
      operationId: public_list_spans
      description: >-
        List spans (public API). Stable API for listing spans by trace_id, span_id, or parent_id.
        Best for retrieving known spans when you already have an ID.
        For advanced filtering with UQL queries (WHERE, search, system filtering),
        use list_spans instead.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: trace_id
          in: query
          description: Filter spans by trace ID.
          schema:
            type: string
          example: 17706d68ea23cf9bc8976ca57d22ee31
        - name: id
          in: query
          description: Filter spans by span ID.
          schema:
            type: integer
          example: 12345
        - name: parent_id
          in: query
          description: Filter spans by parent span ID.
          schema:
            type: integer
          example: 12345
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: List of spans.
          content:
            application/json:
              schema:
                type: object
                properties:
                  spans:
                    type: array
                    description: Array of span objects.
                    items:
                      $ref: "#/components/schemas/Span"
                  hasMore:
                    type: boolean
                    description: Whether more results exist beyond the limit.
                required: [spans]
              example:
                spans:
                  - id: "acb4d95e58413013"
                    traceId: "17706d68ea23cf9bc8976ca57d22ee31"
                    parentId: ""
                    name: GET /api/v1/orders
                    kind: server
                    time: 1688947201000.0
                    duration: 1.2
                    statusCode: ok
                hasMore: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
  /api/v1/tracing/{project_id}/groups:
    get:
      summary: List groups
      x-badges:
        - name: global
      operationId: public_list_span_groups
      description: >-
        List span groups (public API). Stable API for listing aggregated span groups.
        Supports basic search and duration filtering.
        For advanced aggregation with UQL queries (GROUP BY, HAVING, aggregate functions),
        use list_span_groups instead.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: Aggregate spans using the specified query.
          schema:
            type: string
          example: group by host_name
        - $ref: "#/components/parameters/Limit"
        - name: search
          in: query
          description: Search for spans containing option1 or option2.
          schema:
            type: string
          example: option1|option2
        - name: duration_gte
          in: query
          description: Duration greater than or equal to N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 1000
        - name: duration_lt
          in: query
          description: Duration less than N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 5000
      responses:
        "200":
          description: Aggregated spans.
          content:
            application/json:
              schema:
                type: object
                properties:
                  groups:
                    type: array
                    description: Array of group rows with dynamic columns based on the query.
                    items:
                      type: object
                      additionalProperties: true
                  hasMore:
                    type: boolean
                    description: Whether more results exist beyond the limit.
                required: [groups]
              example:
                groups:
                  - host_name: host-1
                    count: 1240
                    errorCount: 12
                    p50: 850
                    p95: 2400
                hasMore: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/spans/{project_id}:
    get:
      summary: List spans
      operationId: list_spans
      description: >-
        List individual spans using UQL (Uptrace Query Language).
        Use this to inspect specific span details, search for errors, or browse recent operations.
        Supports WHERE filters (e.g. where service_name = 'myservice', where _status_code = 'error',
        where _dur_ms > 100ms), full-text search (e.g. word1|word2 -excluded),
        system filtering (e.g. httpserver:all, db:postgresql, log:error),
        duration filtering in milliseconds, and sorting by any span field.
        Span fields use underscore prefix: _name, _dur_ms, _status_code, _time, _trace_id, _kind.
        Attributes use dot-to-underscore: service.name becomes service_name.
        Returns individual span objects with attrs, timing, and status.
        Use list_span_groups instead when you need aggregated metrics (count, avg, p99).
        Use list_traces instead when you need to find traces matching multi-span criteria.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL query to filter spans (e.g., where service_name = 'myservice').
          schema:
            type: string
          example: "where service_name = 'myservice'"
        - name: where
          in: query
          description: Additional WHERE clause appended to the query.
          schema:
            type: string
          example: "service_name = 'myservice'"
        - name: search
          in: query
          description: Full-text search for spans containing the given text.
          schema:
            type: string
          example: "GET /api"
        - name: search_attrs
          in: query
          description: Attribute names to search within when using search. Can be repeated for multiple attributes.
          schema:
            type: array
            items:
              type: string
          example: ["display_name"]
        - name: system
          in: query
          description: Filter by system (e.g., log:error, db:postgresql). Can be repeated for multiple systems.
          schema:
            type: array
            items:
              type: string
          example: ["log:error"]
        - name: sort_by
          in: query
          description: Column names to sort by. Can be repeated for multi-column sort. Defaults to time.
          schema:
            type: array
            items:
              type: string
          example: ["_time"]
        - name: sort_dir
          in: query
          description: Sort direction for each sort_by column. Must match sort_by count. Defaults to DESC.
          schema:
            type: array
            items:
              $ref: '#/components/schemas/SortDirection'
          example: ["DESC"]
        - name: duration_gte
          in: query
          description: Duration greater than or equal to N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 1000
        - name: duration_lt
          in: query
          description: Duration less than N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 5000
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: List of matching spans.
          content:
            application/json:
              schema:
                type: object
                properties:
                  spans:
                    type: array
                    description: Array of span objects.
                    items:
                      $ref: "#/components/schemas/Span"
                  count:
                    type: integer
                    format: int64
                    description: Total count of matching spans.
                  query:
                    type: array
                    description: Parsed query parts with error state.
                    items:
                      type: object
                      additionalProperties: true
                  sorting:
                    type: array
                    description: Applied sorting configuration.
                    items:
                      $ref: '#/components/schemas/OrderItem'
                  search:
                    type: array
                    description: Applied search matchers.
                    items:
                      type: object
                      additionalProperties: true
                  whereAttrs:
                    type: object
                    description: Map of WHERE attribute names to their matched values.
                    additionalProperties: true
                required: [spans, count]
              example:
                spans:
                  - id: "acb4d95e58413013"
                    traceId: "07f3834da9d146dd4d6aea67fe5b1e95"
                    parentId: "6a1445d6b82b6c5d"
                    name: GET /api/v1/orders
                    kind: server
                    time: 1770985573458.9
                    duration: 1.2
                    statusCode: ok
                count: 1
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/spans/{project_id}/groups:
    get:
      summary: List groups
      operationId: list_span_groups
      description: >-
        Aggregate spans into groups using UQL queries.
        Use this to get aggregated metrics like request count, error rate, or latency percentiles.
        Aggregate functions: count(), avg(), sum(), min(), max(),
        p50(), p75(), p90(), p99(), uniq(), apdex().
        Supports GROUP BY (e.g. group by service_name), HAVING (e.g. having p50(_dur_ms) > 100ms),
        WHERE filters, full-text search, system filtering (e.g. httpserver:all, db:postgresql),
        and duration filtering. Example query: 'perMin(count()) | group by host_name'.
        Returns grouped rows with dynamic columns based on the query.
        Use list_spans instead when you need individual span details.
        Use timeseries instead when you need time-bucketed data for charts.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL aggregation query (e.g., perMin(count()) | group by host_name).
          schema:
            type: string
          example: "perMin(count()) | group by host_name"
        - name: where
          in: query
          description: Additional WHERE clause appended to the query.
          schema:
            type: string
          example: "service_name = 'myservice'"
        - name: search
          in: query
          description: Full-text search for spans containing the given text.
          schema:
            type: string
          example: "GET /api"
        - name: search_attrs
          in: query
          description: Attribute names to search within when using search. Can be repeated for multiple attributes.
          schema:
            type: array
            items:
              type: string
          example: ["display_name"]
        - name: system
          in: query
          description: Filter by system (e.g., log:error, db:postgresql). Can be repeated for multiple systems.
          schema:
            type: array
            items:
              type: string
          example: ["spans:all"]
        - name: sort_by
          in: query
          description: Column names to sort by. Can be repeated for multi-column sort.
          schema:
            type: array
            items:
              type: string
          example: ["count"]
        - name: sort_dir
          in: query
          description: Sort direction for each sort_by column. Must match sort_by count. Defaults to DESC.
          schema:
            type: array
            items:
              $ref: '#/components/schemas/SortDirection'
          example: ["DESC"]
        - name: duration_gte
          in: query
          description: Duration greater than or equal to N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 1000
        - name: duration_lt
          in: query
          description: Duration less than N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 5000
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Aggregated span groups.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GroupsResult"
              example:
                groups:
                  - host_name: host-1
                    count: 1240
                    errorCount: 12
                    p50: 850
                    p95: 2400
                columns:
                  - name: count
                    isNum: true
                hasMore: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/spans/{project_id}/timeseries:
    get:
      summary: Timeseries
      operationId: query_timeseries
      description: >-
        Query time-bucketed aggregation data for spans.
        Use this to analyze trends over time, detect anomalies, or build charts.
        Returns aligned timestamps with auto-computed interval based on the time range.
        Use UQL aggregation query (e.g. perMin(count()) | group by service_name)
        with aggregate functions: count(), avg(), sum(), p50(), p90(), p99(), etc.
        Use column parameter to select specific aggregate columns for the timeseries.
        Supports WHERE filters, full-text search, system filtering, and duration filtering.
        Returns groups with arrays of float values aligned with the time array.
        Use list_span_groups instead when you need a single aggregated snapshot (not time-bucketed).
        Use quantiles instead when you only need latency percentiles (p50/p90/p99).
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL aggregation query (e.g., perMin(count()) | group by service_name).
          schema:
            type: string
          example: "perMin(count()) | group by service_name"
        - name: where
          in: query
          description: Additional WHERE clause appended to the query.
          schema:
            type: string
          example: "service_name = 'myservice'"
        - name: search
          in: query
          description: Full-text search for spans containing the given text.
          schema:
            type: string
          example: "GET /api"
        - name: search_attrs
          in: query
          description: Attribute names to search within when using search. Can be repeated for multiple attributes.
          schema:
            type: array
            items:
              type: string
          example: ["display_name"]
        - name: system
          in: query
          description: Filter by system (e.g., log:error, db:postgresql). Can be repeated for multiple systems.
          schema:
            type: array
            items:
              type: string
          example: ["spans:all"]
        - name: column
          in: query
          description: Aggregate column names to include in the timeseries. Can be repeated for multiple columns.
          schema:
            type: array
            items:
              type: string
          example: ["count"]
        - name: duration_gte
          in: query
          description: Duration greater than or equal to N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 10000
        - name: duration_lt
          in: query
          description: Duration less than N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 100000
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Timeseries data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  groups:
                    type: array
                    description: Array of group rows. Aggregated columns contain arrays of float values aligned with the time array.
                    items:
                      type: object
                      additionalProperties: true
                  columns:
                    type: array
                    description: Column definitions for the result.
                    items:
                      $ref: "#/components/schemas/QueryColumn"
                  time:
                    type: array
                    description: Array of aligned timestamps as unix milliseconds.
                    items:
                      type: number
                      format: double
                  interval:
                    type: integer
                    format: int64
                    description: Time interval between data points in milliseconds.
                  query:
                    type: array
                    description: Parsed query parts with error state.
                    items:
                      type: object
                      additionalProperties: true
                  sorting:
                    type: array
                    description: Applied sorting configuration.
                    items:
                      $ref: '#/components/schemas/OrderItem'
                  whereAttrs:
                    type: object
                    description: Map of WHERE attribute names to their matched values.
                    additionalProperties: true
                required: [groups, time, interval]
              example:
                groups:
                  - service_name: myservice
                    "perMin(count())": [10.5, 20.3, 15.1, 30.7]
                time: [1688947200000, 1688947260000, 1688947320000, 1688947380000]
                interval: 60000
                columns:
                  - expr: "perMin(count())"
                    name: "perMin(count())"
                    isAgg: true
                    isNum: true
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/spans/{project_id}/quantiles:
    get:
      summary: Quantiles
      operationId: query_quantiles
      description: >-
        Query duration percentiles (p50, p90, p99) and count/error rate over time.
        Use this to identify latency outliers, track performance degradation, or compare SLO compliance.
        Returns named timeseries: count, countPerMin, errorCount, errorCountPerMin,
        durationP50, durationP90, durationP99, durationMax.
        Supports WHERE filters (e.g. where service_name = 'myservice'),
        full-text search, system filtering (e.g. httpserver:all), and duration filtering.
        Use timeseries instead when you need custom aggregation queries with GROUP BY.
        Use list_span_groups instead when you need a single aggregated snapshot.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL query to filter spans (e.g., where service_name = 'myservice').
          schema:
            type: string
          example: "where service_name = 'myservice'"
        - name: where
          in: query
          description: Additional WHERE clause appended to the query.
          schema:
            type: string
          example: "service_name = 'myservice'"
        - name: search
          in: query
          description: Full-text search for spans containing the given text.
          schema:
            type: string
          example: "GET /api"
        - name: search_attrs
          in: query
          description: Attribute names to search within when using search. Can be repeated for multiple attributes.
          schema:
            type: array
            items:
              type: string
          example: ["display_name"]
        - name: system
          in: query
          description: Filter by system (e.g., log:error, db:postgresql). Can be repeated for multiple systems.
          schema:
            type: array
            items:
              type: string
          example: ["spans:all"]
        - name: duration_gte
          in: query
          description: Duration greater than or equal to N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 10000
        - name: duration_lt
          in: query
          description: Duration less than N (milliseconds).
          schema:
            type: integer
            format: int64
          example: 100000
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Quantile timeseries data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  timeseries:
                    type: array
                    description: >-
                      Array of named timeseries. Names include: count, countPerMin, errorCount, errorCountPerMin,
                      durationP50, durationP90, durationP99, durationMax.
                    items:
                      $ref: "#/components/schemas/Timeseries"
                required: [timeseries]
              example:
                timeseries:
                  - name: durationP50
                    value: [1.2, 1.3, 1.1]
                    time: [1688947200000, 1688947260000, 1688947320000]
                  - name: countPerMin
                    value: [100, 120, 90]
                    time: [1688947200000, 1688947260000, 1688947320000]
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/traces/{project_id}/groups:
    get:
      summary: List trace groups
      operationId: list_trace_groups
      description: >-
        Aggregate traces into groups using correlated sub-queries.
        Use this to find trace patterns and get aggregated trace metrics.
        Requires parallel arrays: query[], alias[], system[] with matching lengths.
        One alias must be 'root' to identify the root span query.
        Additional sub-queries filter traces where child spans match specific criteria.
        Systems: spans:all, httpserver:all, db:postgresql, log:error, etc.
        Returns grouped rows with dynamic columns.
        Use list_traces instead when you need individual trace details.
        Use list_span_groups instead when you don't need cross-span trace correlation.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL query for each sub-query. Must have the same count as alias and system. Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["where service_name = 'myservice'"]
        - name: alias
          in: query
          description: Alias for each sub-query. One alias must be 'root'. Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["root"]
        - name: system
          in: query
          description: System for each sub-query (e.g., spans:all, log:error). Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["spans:all"]
        - name: sort_by
          in: query
          description: Column names to sort by. Can be repeated for multi-column sort.
          schema:
            type: array
            items:
              type: string
          example: ["count"]
        - name: sort_dir
          in: query
          description: Sort direction for each sort_by column. Must match sort_by count. Defaults to DESC.
          schema:
            type: array
            items:
              $ref: '#/components/schemas/SortDirection'
          example: ["DESC"]
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Trace groups result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GroupsResult"
              example:
                groups:
                  - _group_id: "100"
                    count: 42
                    errorCount: 2
                columns:
                  - name: count
                    isNum: true
                hasMore: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/traces/{project_id}:
    get:
      summary: List traces
      operationId: list_traces
      description: >-
        List individual traces using correlated sub-queries.
        Use this to find specific traces matching complex multi-span criteria.
        Requires parallel arrays: query[], alias[], system[] with matching lengths.
        One alias must be 'root' to identify the root span query.
        Additional sub-queries filter traces where child spans match specific criteria.
        Systems: spans:all, httpserver:all, db:postgresql, log:error, etc.
        Returns root spans for matching traces sorted by time (DESC by default).
        Use list_trace_groups instead when you need aggregated trace metrics.
        Use list_spans instead when you don't need cross-span trace correlation.
        Documentation: https://uptrace.dev/features/querying/spans
      tags: [Querying]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: query
          in: query
          description: UQL query for each sub-query. Must have the same count as alias and system. Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["where service_name = 'myservice'"]
        - name: alias
          in: query
          description: Alias for each sub-query. One alias must be 'root'. Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["root"]
        - name: system
          in: query
          description: System for each sub-query (e.g., spans:all, log:error). Can be repeated.
          schema:
            type: array
            items:
              type: string
          example: ["spans:all"]
        - name: sort_by
          in: query
          description: Column names to sort by. Can be repeated for multi-column sort. Defaults to time.
          schema:
            type: array
            items:
              type: string
          example: ["_time"]
        - name: sort_dir
          in: query
          description: Sort direction for each sort_by column. Must match sort_by count. Defaults to DESC.
          schema:
            type: array
            items:
              $ref: '#/components/schemas/SortDirection'
          example: ["DESC"]
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: List of matching traces.
          content:
            application/json:
              schema:
                type: object
                properties:
                  spans:
                    type: array
                    description: Root spans for matching traces.
                    items:
                      $ref: "#/components/schemas/Span"
                  count:
                    type: integer
                    format: int64
                    description: Total count of matching traces.
                  query:
                    type: array
                    description: Parsed query parts with error state.
                    items:
                      type: object
                      additionalProperties: true
                  join:
                    type: array
                    description: >-
                      Parsed sub-query clauses for each non-root joined alias. One entry
                      per non-root sub-query passed in query[]/alias[]/system[].
                    items:
                      type: array
                      items:
                        type: object
                        additionalProperties: true
                  sorting:
                    type: array
                    description: Applied sorting configuration.
                    items:
                      $ref: '#/components/schemas/OrderItem'
                  search:
                    type: array
                    description: Applied search matchers.
                    items:
                      type: object
                      additionalProperties: true
                  whereAttrs:
                    type: object
                    description: Map of WHERE attribute names to their matched values.
                    additionalProperties: true
                required: [spans, count]
              example:
                spans:
                  - id: "298ec3d46392d4b7"
                    traceId: "103ba7cd7900cdf2b04e842e15ad9702"
                    name: GET /api/v1/orders
                    displayName: GET /api/v1/orders
                    time: 1770985602526.471
                    duration: 1.634
                    statusCode: ok
                count: 1
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /api/v1/fixtures:
    put:
      summary: Sync fixtures
      operationId: sync_fixtures
      description:
        Create, update, or delete users, orgs, projects, and related resources
        using fixture keys.
      tags: [Fixtures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SeedDataRequest"
            example:
              users:
                - key: admin_user
                  name: System Administrator
                  email: admin@company.com
                  password: change_me_immediately
              orgs:
                - key: main_org
                  name: Acme Corporation
              orgUsers:
                - key: admin_membership
                  orgKey: main_org
                  userKey: admin_user
                  role: owner
              projects:
                - key: web_app_project
                  name: Web Application
                  orgKey: main_org
      responses:
        "200":
          description: Fixtures applied successfully.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /api/v1/fixtures/bindings:
    put:
      summary: Bind existing resources to fixture keys
      operationId: bind_fixture_keys
      description: Assign fixture keys to existing resources created outside of fixtures.
      tags: [Fixtures]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LinkDataRequest"
            example:
              orgs:
                - id: 123
                  key: legacy_org
              projects:
                - id: 456
                  key: existing_project
      responses:
        "200":
          description: Bindings applied successfully.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/monitors/{project_id}:
    get:
      summary: List monitors
      x-badges:
        - name: beta
      operationId: list_monitors
      description: >-
        List monitoring rules and alerts configured in Uptrace.
        Use this to review alert configurations, check notification channels,
        and understand monitoring thresholds.
        Returns monitors with their type, state, query, and notification settings.
        Use list_dashboards instead when looking for visualization dashboards.
        Documentation: https://uptrace.dev/features/alerting
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: search
          in: query
          required: false
          description: Free-text search applied to monitor names and attributes.
          schema:
            type: string
        - name: status
          in: query
          required: false
          description: Filter monitors by status (e.g. active, paused, failing).
          schema:
            type: string
        - name: sort_by
          in: query
          required: false
          description: Column to sort by.
          schema:
            type: string
        - name: sort_dir
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/SortDirection"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: List of monitors.
          content:
            application/json:
              schema:
                type: object
                required:
                  - monitors
                properties:
                  monitors:
                    type: array
                    items:
                      $ref: "#/components/schemas/Monitor"
                  statuses:
                    type: array
                    description: Per-status monitor counts across the project (ignores the status filter).
                    items:
                      $ref: "#/components/schemas/MonitorStatusCount"
                  count:
                    type: integer
                    format: int64
                    description: Total number of monitors matching the current filter.
                  search:
                    type: array
                    description: Parsed search matchers applied to the request.
                    items:
                      type: object
                      additionalProperties: true
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create a monitor
      x-badges:
        - name: beta
      operationId: create_monitor
      description: Create a metric or error monitor.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/MetricMonitorRequest"
                - $ref: "#/components/schemas/ErrorMonitorRequest"
            examples:
              metricMonitor:
                summary: Metric monitor
                value:
                  name: Number of spans per minute
                  type: metric
                  notifyEveryoneByEmail: true
                  teamIds: [123]
                  channelIds: [123]
                  repeatInterval:
                    strategy: default
                  params:
                    metrics:
                      - name: uptrace_tracing_spans
                        alias: $spans
                    query:
                      "perMin(count($spans)) as spans | where service_name =
                      'myservice'"
                    column:
                      name: spans
                    detector:
                      type: manual
                      params:
                        minValue: 100
                        maxValue: 1000
              errorMonitor:
                summary: Error monitor
                value:
                  name: Number of spans per minute
                  type: error
                  notifyEveryoneByEmail: true
                  teamIds: [123]
                  channelIds: [123]
                  repeatInterval:
                    strategy: default
                  params:
                    metrics:
                      - name: uptrace_tracing_logs
                        alias: $logs
                    query: "group by _group_id | where _system in ('log:error', 'log:fatal')"
      responses:
        "200":
          description: Monitor created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
              example:
                monitor:
                  id: 3807
                  name: Number of spans per minute
                  type: metric
                  status: active
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
  /internal/v1/monitors/{project_id}/{monitor_id}:
    get:
      summary: Get a monitor
      x-badges:
        - name: beta
      operationId: get_monitor
      description: Retrieve a specific monitor by ID.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/MonitorId"
      responses:
        "200":
          description: Monitor details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update a monitor
      x-badges:
        - name: beta
      operationId: update_monitor
      description: Update a metric or error monitor by ID.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/MonitorId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/MetricMonitorRequest"
                - $ref: "#/components/schemas/ErrorMonitorRequest"
            examples:
              metricMonitor:
                summary: Metric monitor
                value:
                  name: Number of spans per minute
                  type: metric
                  notifyEveryoneByEmail: true
                  teamIds: [123]
                  channelIds: [123]
                  repeatInterval:
                    strategy: default
                  params:
                    metrics:
                      - name: uptrace_tracing_spans
                        alias: $spans
                    query:
                      "perMin(count($spans)) as spans | where service_name =
                      'myservice'"
                    column:
                      name: spans
                    detector:
                      type: manual
                      params:
                        minValue: 100
                        maxValue: 1000
              errorMonitor:
                summary: Error monitor
                value:
                  name: Number of spans per minute
                  type: error
                  notifyEveryoneByEmail: true
                  teamIds: [123]
                  channelIds: [123]
                  repeatInterval:
                    strategy: default
                  params:
                    metrics:
                      - name: uptrace_tracing_logs
                        alias: $logs
                    query: "group by _group_id | where _system in ('log:error', 'log:fatal')"
      responses:
        "200":
          description: Monitor updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
              example:
                monitor:
                  id: 3807
                  name: Number of spans per minute
                  type: metric
                  status: active
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete a monitor
      x-badges:
        - name: beta
      operationId: delete_monitor
      description: Delete a metric or error monitor by ID.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/MonitorId"
      responses:
        "200":
          description: Monitor deleted. Returns the deleted monitor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/monitors/{project_id}/{monitor_id}/active:
    put:
      summary: Activate a monitor
      x-badges:
        - name: beta
      operationId: activate_monitor
      description: Resume a monitor and set its status to active.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/MonitorId"
      responses:
        "200":
          description: Monitor activated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/monitors/{project_id}/{monitor_id}/paused:
    put:
      summary: Pause a monitor
      x-badges:
        - name: beta
      operationId: pause_monitor
      description: >-
        Pause a monitor.
        When `duration` is provided, the monitor automatically resumes after that period;
        otherwise it stays paused until activated explicitly.
      tags: [Monitors]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/MonitorId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                duration:
                  type: integer
                  format: int64
                  minimum: 3600000000000
                  description: >-
                    Pause duration in nanoseconds.
                    Minimum 1 hour (3600000000000 ns).
                    Omit for an indefinite pause.
            examples:
              indefinite:
                summary: Pause indefinitely
                value: {}
              forTwoHours:
                summary: Pause for 2 hours
                value:
                  duration: 7200000000000
      responses:
        "200":
          description: Monitor paused.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}:
    get:
      summary: List dashboards
      x-badges:
        - name: beta
      operationId: list_dashboards
      description: >-
        List dashboards configured in Uptrace.
        Use this to browse available dashboards for monitoring and visualization.
        Returns dashboard summaries with ID, name, and tags.
        Use get_dashboard with a specific ID to retrieve full dashboard details including grid rows and items.
        Use create_dashboard to create a new dashboard from YAML.
        Documentation: https://uptrace.dev/features/dashboards
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: q
          in: query
          required: false
          description: Free-text search applied to dashboard name and indexed columns.
          schema:
            type: string
        - name: pinned
          in: query
          required: false
          description: When true, return only pinned dashboards.
          schema:
            type: boolean
        - name: tag
          in: query
          required: false
          description: Filter by dashboard tag. Repeat to match any of multiple tags.
          schema:
            type: array
            items:
              type: string
        - name: sort_by
          in: query
          required: false
          description: Column to sort by (e.g. name, updatedAt). Defaults to updatedAt.
          schema:
            type: string
        - name: sort_dir
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/SortDirection"
      responses:
        "200":
          description: List of dashboards.
          content:
            application/json:
              schema:
                type: object
                required:
                  - dashboards
                properties:
                  dashboards:
                    type: array
                    items:
                      $ref: "#/components/schemas/Dashboard"
                  noData:
                    type: boolean
                    description: True when no dashboards exist for the project at all (independent of the current filter).
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/tags:
    get:
      summary: List dashboard tags
      x-badges:
        - name: beta
      operationId: list_dashboard_tags
      description: >-
        List all available dashboard tags for a project.
        Tags are simple string labels used for categorizing and filtering dashboards.
        Use these tags when creating new dashboards to ensure consistent categorization.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: List of available tags.
          content:
            application/json:
              schema:
                type: object
                required:
                  - tags
                properties:
                  tags:
                    type: array
                    items:
                      type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboard-templates/schema:
    get:
      summary: Get dashboard template JSON Schema
      operationId: get_dashboard_template_schema
      description: >-
        Get the JSON Schema for validating dashboard template YAML files.
        Returns the schema as application/schema+json.
      tags: [Dashboards]
      responses:
        "200":
          description: Dashboard template JSON Schema.
          content:
            application/schema+json:
              schema:
                type: object

  /internal/v1/dashboard-templates/{project_id}:
    get:
      summary: List dashboard templates
      operationId: list_dashboard_templates
      description: >-
        List all available built-in dashboard templates.
        Returns template ID, name, and description for each template.
        Use this to discover available templates before creating dashboards.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: q
          in: query
          required: false
          description: Free-text search applied to template name and description.
          schema:
            type: string
        - name: tag
          in: query
          required: false
          description: Filter by template tag. Repeat to match any of multiple tags.
          schema:
            type: array
            items:
              type: string
        - name: status
          in: query
          required: false
          description: Filter by template status.
          schema:
            $ref: "#/components/schemas/TemplateStatus"
      responses:
        "200":
          description: Dashboard templates list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListDashboardTemplatesResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /internal/v1/dashboard-templates/{project_id}/{template_id}:
    get:
      summary: Get dashboard template
      operationId: get_dashboard_template
      description: >-
        Get a specific dashboard template by ID.
        Returns the template ID, name, description, and tags.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: template_id
          in: path
          required: true
          description: Dashboard template ID.
          schema:
            type: string
      responses:
        "200":
          description: Dashboard template.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetDashboardTemplateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /internal/v1/metrics/{project_id}/explore:
    get:
      summary: Explore metrics
      operationId: explore_metrics
      description: >-
        Discover available metrics and their metadata.
        Returns metric names, instrument types (histogram, counter, gauge, additive),
        units, descriptions, available attributes, and library info.
        Use the instrument type to choose the correct aggregate function:
        histogram metrics support count(), p50(), p90(), p99();
        counter metrics support sum(), perMin(sum());
        gauge metrics support sum(), avg(), max(), min();
        additive metrics support sum(), perMin(sum()).
        Use the search parameter to filter by metric name.
        Use this tool before creating dashboards to discover metrics and their attributes.
      tags: [Metrics]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: search
          in: query
          required: false
          description: Search string to filter metrics by name (substring match).
          schema:
            type: string
        - name: instrument
          in: query
          required: false
          description: >-
            Filter by instrument type. Can be repeated to pass multiple values
            (e.g. instrument=counter&instrument=gauge). Allowed values:
            histogram, counter, gauge, additive.
          schema:
            type: array
            items:
              type: string
          example: ["counter"]
        - name: library_name
          in: query
          required: false
          description: >-
            Filter by instrumentation library name (exact match). Can be repeated;
            results match any of the listed libraries.
          schema:
            type: array
            items:
              type: string
          example: ["go.opentelemetry.io/contrib/instrumentation/runtime"]
        - name: attr_key_exists
          in: query
          required: false
          description: >-
            Return only metrics that expose all of the listed attribute keys (AND
            semantics). Uptrace normalizes attribute keys by replacing dots with
            underscores (service.name → service_name), so pass the underscored form.
          schema:
            type: array
            items:
              type: string
          example: ["host_name"]
      responses:
        "200":
          description: Metrics list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExploreMetricsResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /internal/v1/metrics/{project_id}/attributes:
    get:
      summary: List metric attributes
      operationId: list_metric_attributes
      description: >-
        List available attribute keys for metrics.
        Returns attribute keys with their kind (str, int, float), unit, and count (how many metrics use each attribute).
        Use the search parameter to filter by metric name.
        Use this to discover which attributes are available for group by in dashboard queries.
      tags: [Metrics]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: search
          in: query
          required: false
          description: Search string to filter metrics by name.
          schema:
            type: string
      responses:
        "200":
          description: Attribute keys list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListMetricAttributesResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /internal/v1/metrics/{project_id}/attributes/{attr_key}:
    get:
      summary: List attribute values
      operationId: list_metric_attribute_values
      description: >-
        List values for a specific metric attribute key.
        Returns distinct attribute values with their occurrence count.
        Use the search parameter to filter by metric name.
      tags: [Metrics]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TimeStart"
        - $ref: "#/components/parameters/TimeEnd"
        - name: attr_key
          in: path
          required: true
          description: "Attribute key including type suffix (e.g. host_name::str, http_status_code::int)."
          schema:
            type: string
        - name: search
          in: query
          required: false
          description: Search string to filter metrics by name.
          schema:
            type: string
      responses:
        "200":
          description: Attribute values list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListMetricAttributeValuesResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /internal/v1/dashboards/{project_id}/yaml:
    post:
      summary: Create dashboard from YAML
      x-badges:
        - name: beta
      operationId: create_dashboard_from_yaml
      description: >-
        Create a new dashboard from YAML definition.
        Use this to create visualization dashboards for spans, events, logs, and metrics.
        Supports grid-based and table-based layouts with PromQL-style metric queries.
        The YAML body must include: schema (v2 or v3) and name. Optional fields: version, tags.
        Use get_dashboard_yaml on an existing dashboard to see the exact YAML format before creating.
        The API strictly rejects unknown fields.
        Table and table_grid_items overrides use column (required, must match a query alias) + properties (array of {name, value}).
        Grid item overrides (in grid_rows) use matchers + properties (array of {name, value}).
        Sparkline is a table column property ({name: sparkline, value: true}), not a top-level field.
        Documentation: https://uptrace.dev/features/dashboards
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          text/yaml:
            schema:
              type: string
              description: YAML dashboard definition.
      responses:
        "200":
          description: Dashboard created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}:
    get:
      summary: Get a dashboard
      x-badges:
        - name: beta
      operationId: get_dashboard
      description: >-
        Get a dashboard by ID from Uptrace.
        Use this to retrieve full dashboard details including grid rows, items, and metric queries.
        Requires a dashboard_id -- use list_dashboards first to find available dashboard IDs.
        Documentation: https://uptrace.dev/features/dashboards
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard details.
          content:
            application/json:
              schema:
                type: object
                required:
                  - dashboard
                properties:
                  dashboard:
                    $ref: "#/components/schemas/Dashboard"
                  tableItems:
                    type: array
                    items:
                      $ref: "#/components/schemas/GridItem"
                  gridRows:
                    type: array
                    items:
                      $ref: "#/components/schemas/GridSection"
                  gridMetrics:
                    type: array
                    items:
                      type: string
                  yamlUrl:
                    type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete a dashboard
      x-badges:
        - name: beta
      operationId: delete_dashboard
      description: Delete a dashboard by ID.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard deleted. Returns the deleted dashboard.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/yaml:
    get:
      summary: Get dashboard YAML
      x-badges:
        - name: beta
      operationId: get_dashboard_yaml
      description: Retrieve the YAML representation of a dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard YAML.
          content:
            text/yaml:
              schema:
                type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update dashboard from YAML
      x-badges:
        - name: beta
      operationId: update_dashboard_from_yaml
      description: >-
        Update an existing dashboard from YAML definition.
        Use get_dashboard_yaml first to retrieve the current YAML, then modify and submit.
        The API strictly rejects unknown fields.
        Table overrides use column + properties (array of {name, value}).
        Grid item overrides use matchers + properties (array of {name, value}).
        Documentation: https://uptrace.dev/features/dashboards
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          text/yaml:
            schema:
              type: string
              description: YAML dashboard definition.
      responses:
        "200":
          description: Dashboard updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/clone:
    post:
      summary: Clone a dashboard
      x-badges:
        - name: beta
      operationId: clone_dashboard
      description: Create a copy of an existing dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard cloned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/reset-layout:
    put:
      summary: Reset a dashboard
      x-badges:
        - name: beta
      operationId: reset_dashboard
      description: Reset dashboard to template or reset layout.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard reset.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/pinned:
    put:
      summary: Pin a dashboard
      x-badges:
        - name: beta
      operationId: pin_dashboard
      description: Pin dashboard to top of dashboard list.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard pinned.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/unpinned:
    put:
      summary: Unpin a dashboard
      x-badges:
        - name: beta
      operationId: unpin_dashboard
      description: Unpin dashboard from top of dashboard list.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      responses:
        "200":
          description: Dashboard unpinned.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/table:
    put:
      summary: Update dashboard table
      x-badges:
        - name: beta
      operationId: update_dashboard_table
      description: Update the table configuration of a dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DashboardTableUpdate"
      responses:
        "200":
          description: Dashboard table updated.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/grid:
    put:
      summary: Update dashboard grid query
      x-badges:
        - name: beta
      operationId: update_dashboard_grid
      description: Update the global grid query filter for a dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                gridQuery:
                  type: string
      responses:
        "200":
          description: Dashboard grid query updated.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create grid item
      x-badges:
        - name: beta
      operationId: create_grid_item
      description: Create a new grid item in the dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GridItemRequest"
      responses:
        "200":
          description: Grid item created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridItemResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/grid/layout:
    put:
      summary: Update grid layout
      x-badges:
        - name: beta
      operationId: update_grid_layout
      description: Update the position and size of grid items within a section.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                sectionId:
                  type: integer
                  format: int64
                  description: Section ID to update layout for.
                items:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: integer
                        format: int64
                        description: Grid item ID.
                      width:
                        type: integer
                      height:
                        type: integer
                      xAxis:
                        type: integer
                      yAxis:
                        type: integer
                    required:
                      - id
                      - width
                      - height
                      - xAxis
                      - yAxis
              required:
                - sectionId
                - items
      responses:
        "200":
          description: Grid layout updated.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/grid/{grid_item_id}:
    put:
      summary: Update grid item
      x-badges:
        - name: beta
      operationId: update_grid_item
      description: Update an existing grid item.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/GridItemId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GridItemRequest"
      responses:
        "200":
          description: Grid item updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridItemResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete grid item
      x-badges:
        - name: beta
      operationId: delete_grid_item
      description: Delete a grid item from the dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/GridItemId"
      responses:
        "200":
          description: Grid item deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridItemResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/sections:
    post:
      summary: Create grid section
      x-badges:
        - name: beta
      operationId: create_grid_section
      description: Create a new grid section in the dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GridSectionRequest"
      responses:
        "200":
          description: Grid section created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/sections/{section_id}:
    put:
      summary: Update grid section
      x-badges:
        - name: beta
      operationId: update_grid_section
      description: Update an existing grid section.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/SectionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GridSection"
      responses:
        "200":
          description: Grid section updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete grid section
      x-badges:
        - name: beta
      operationId: delete_grid_section
      description: Delete a grid section and all its items.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/SectionId"
      responses:
        "200":
          description: Grid section deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/sections/{section_id}/up:
    put:
      summary: Move grid section up
      x-badges:
        - name: beta
      operationId: move_grid_section_up
      description: Move a grid section up in the dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/SectionId"
      responses:
        "200":
          description: Grid section moved up.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/dashboards/{project_id}/{dashboard_id}/sections/{section_id}/down:
    put:
      summary: Move grid section down
      x-badges:
        - name: beta
      operationId: move_grid_section_down
      description: Move a grid section down in the dashboard.
      tags: [Dashboards]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/DashboardId"
        - $ref: "#/components/parameters/SectionId"
      responses:
        "200":
          description: Grid section moved down.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GridSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/alerts/{project_id}:
    get:
      summary: List alerts
      x-badges:
        - name: beta
      operationId: list_alerts
      description: >-
        List alerts with optional filtering by status, priority, type, activity state,
        monitor ID, and free-text search. Returns alerts sorted by most recent event by default.
      tags: [Alerts]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - name: search
          in: query
          description: Free-text search across alert name and attributes. Supports scoped search (e.g., alert.status:resolved).
          schema:
            type: string
        - name: attrs[status][]
          in: query
          description: Filter by alert status. Can be repeated for multiple values.
          schema:
            type: array
            items:
              $ref: "#/components/schemas/AlertStatus"
        - name: attrs[priority][]
          in: query
          description: Filter by alert priority. Can be repeated for multiple values.
          schema:
            type: array
            items:
              $ref: "#/components/schemas/AlertPriority"
        - name: attrs[type][]
          in: query
          description: Filter by alert type. Can be repeated for multiple values.
          schema:
            type: array
            items:
              $ref: "#/components/schemas/AlertType"
        - name: attrs[activityState][]
          in: query
          description: Filter by activity state. Can be repeated for multiple values.
          schema:
            type: array
            items:
              $ref: "#/components/schemas/AlertActivityState"
        - name: attrs[monitorId][]
          in: query
          description: Filter by monitor ID. Can be repeated for multiple values.
          schema:
            type: array
            items:
              type: integer
              format: uint64
        - name: sort_by
          in: query
          description: Field to sort by. Defaults to event.created_at.
          schema:
            type: string
          example: created_at
        - name: sort_dir
          in: query
          description: Sort direction. Defaults to DESC.
          schema:
            $ref: "#/components/schemas/SortDirection"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: List of alerts.
          content:
            application/json:
              schema:
                type: object
                required:
                  - alerts
                  - count
                properties:
                  alerts:
                    type: array
                    items:
                      $ref: "#/components/schemas/Alert"
                  count:
                    type: integer
                    description: Total number of matching alerts.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/alerts/{project_id}/{alert_id}:
    get:
      summary: Get an alert
      x-badges:
        - name: beta
      operationId: get_alert
      description: Retrieve a specific alert by ID with full event history and assignees.
      tags: [Alerts]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/AlertId"
      responses:
        "200":
          description: Alert details with event history.
          content:
            application/json:
              schema:
                type: object
                required:
                  - alert
                  - events
                  - assignees
                properties:
                  alert:
                    $ref: "#/components/schemas/Alert"
                  events:
                    type: array
                    items:
                      $ref: "#/components/schemas/AlertEvent"
                  assignees:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs:
    get:
      summary: List organizations
      operationId: list_orgs
      description: List all organizations accessible to the authenticated user.
      tags: [Organizations]
      x-badges:
        - name: beta
      responses:
        "200":
          description: Organizations listed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  orgs:
                    type: array
                    items:
                      $ref: "#/components/schemas/Org"
                required:
                  - orgs
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create organization
      operationId: create_org
      description: Create a new organization.
      tags: [Organizations]
      x-badges:
        - name: beta
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgCreateRequest"
      responses:
        "200":
          description: Organization created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}:
    get:
      summary: Get organization
      operationId: get_org
      description: Get organization details.
      tags: [Organizations]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      responses:
        "200":
          description: Organization details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update organization
      operationId: update_org
      description: Update organization settings.
      tags: [Organizations]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUpdateRequest"
      responses:
        "200":
          description: Organization updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete organization
      operationId: delete_org
      description: Delete an organization. Fails if the organization contains projects.
      tags: [Organizations]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      responses:
        "200":
          description: Organization deleted. Returns the deleted organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/budget:
    put:
      summary: Update organization budget
      operationId: update_org_budget
      description: Update the organization's budget. Values below the minimum budget are clamped to the minimum.
      tags: [Organizations]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUpdateBudgetRequest"
      responses:
        "200":
          description: Budget updated. Returns the updated organization.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/projects:
    get:
      summary: List organization projects
      operationId: list_org_projects
      description: List all projects in an organization.
      tags: [Organizations]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      responses:
        "200":
          description: Projects listed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  projects:
                    type: array
                    items:
                      $ref: "#/components/schemas/Project"
                required:
                  - projects
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create project
      operationId: create_project
      description: Create a new project within an organization.
      tags: [Projects]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectCreateRequest"
      responses:
        "200":
          description: Project created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/users:
    get:
      summary: List organization users
      operationId: list_org_users
      description: List all users that belong to an organization.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - name: email
          in: query
          required: false
          description: Case-insensitive substring match against user email.
          schema:
            type: string
      responses:
        "200":
          description: Users listed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgUserListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Add organization user by ID
      operationId: create_org_user
      description: >-
        Create or update an OrgUser membership for an existing User. Idempotent:
        if the user is already a member, their role is updated to the supplied
        value. Used by automation that holds a userId directly. Requires org
        admin. Accepts users with email_confirmed=false (e.g. invited but not
        yet accepted).
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUserCreateRequest"
      responses:
        "200":
          description: Membership created or role updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgUserCreateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/users/{org_user_id}:
    get:
      summary: Get organization user
      operationId: get_org_user
      description: Get detailed information about an organization user, including their project and team memberships.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/OrgUserId"
      responses:
        "200":
          description: Organization user details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgUserShowResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update organization user role
      operationId: update_org_user_role
      description: >-
        Update a user's organization-level role. Requires org admin.
        An admin cannot change their own role.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/OrgUserId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUserRoleUpdateRequest"
      responses:
        "200":
          description: Role updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgUserResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Remove organization user
      operationId: remove_org_user
      description: >-
        Remove a user from an organization. Requires org admin. Also cancels any
        pending invitations for the user's email in this organization.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/OrgUserId"
      responses:
        "200":
          description: User removed from organization. Returns an empty object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmptyResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/users/{org_user_id}/projects/{project_id}:
    put:
      summary: Update organization user project permission
      operationId: update_org_user_project_perm
      description: >-
        Set a user's project-level permission override. Requires org admin.
        Upserts on (org_user_id, project_id).
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/OrgUserId"
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUserProjectUpdateRequest"
      responses:
        "200":
          description: Permission updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgUserProjectResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/invites:
    get:
      summary: List organization invitations
      operationId: list_org_invites
      description: >-
        List all invitations for an organization. If the caller does not have
        `CanInviteUsers` permission on the org, returns an empty list.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      responses:
        "200":
          description: Invitations listed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserInviteListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create organization invitation
      operationId: create_org_invite
      description: >-
        Invite a user to the organization by email. Requires org admin. Behavior:

        - If the email maps to an already-confirmed User, attach them as an
          OrgUser (and to any teamIds) immediately. The response includes a
          compact invite summary with status `accepted` and orgId set to the
          attached org; no invite row is inserted.

        - If the email maps to an unconfirmed User with a live pending invite for
          this org, return the existing invite with status `sent`. If the
          existing invite is canceled or expired, reactivate it with status
          `sent`.

        - Otherwise create the User row (with `email_confirmed=false` if missing),
          create a UserInvite row, send the invitation email, and return it with
          status `sent`.

        Subject to a per-inviter cap of 10 pending invites. Every non-accepted
        invite within the invite TTL counts toward the cap.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UserInviteCreateRequest"
      responses:
        "200":
          description: Invitation created (email sent) or immediately accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserInviteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/invites/{invite_id}:
    delete:
      summary: Cancel organization invitation
      operationId: cancel_org_invite
      description: Cancel a pending invitation. Requires org admin.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/InviteId"
      responses:
        "200":
          description: Invitation canceled. Returns the canceled invitation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserInviteCancelResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/invites/{invite_id}/resend:
    post:
      summary: Resend organization invitation
      operationId: resend_org_invite
      description: >-
        Bump a sent or canceled invite's expiry to now and re-send the
        invitation email. Preserves the same invite ID and `invited_by` so the
        join link stays valid. Requires org admin. Canceled invites are restored
        to `sent`; `accepted` and `expired` invites return 400.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/InviteId"
      responses:
        "200":
          description: Invitation re-sent.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResendInviteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams:
    get:
      summary: List organization teams
      operationId: list_teams
      description: List all teams in an organization. Teams are a Premium feature; non-admin callers receive an empty list.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      responses:
        "200":
          description: Teams listed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  teams:
                    type: array
                    items:
                      $ref: "#/components/schemas/Team"
                required:
                  - teams
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create team
      operationId: create_team
      description: Create a new team in an organization. Requires org admin.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamCreateRequest"
      responses:
        "200":
          description: Team created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams/{team_id}:
    get:
      summary: Get team
      operationId: get_team
      description: Get team details.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
      responses:
        "200":
          description: Team details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update team
      operationId: update_team
      description: >-
        Update a team's name and permission level. Requires org admin.
        Omitting or sending an empty `name` or `permLevel` preserves the
        existing value for that field.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamUpdateRequest"
      responses:
        "200":
          description: Team updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete team
      operationId: delete_team
      description: Delete a team. Requires org admin. Returns the deleted team.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
      responses:
        "200":
          description: Team deleted. Returns the deleted team.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams/{team_id}/projects:
    get:
      summary: List team projects
      operationId: list_team_projects
      description: List all projects a team has access to.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
      responses:
        "200":
          description: Projects listed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  projects:
                    type: array
                    items:
                      $ref: "#/components/schemas/Project"
                required:
                  - projects
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams/{team_id}/projects/{project_id}:
    post:
      summary: Add project to team
      operationId: add_team_project
      description: >-
        Grant a team access to a project. Idempotent (no-op if already added).
        Requires org admin plus `CanInviteUsers` on the project.
        Request body is required but its `permLevel` field is currently accepted and ignored server-side.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamProjectAddRequest"
      responses:
        "200":
          description: Project added to team. Returns an empty object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmptyResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Remove project from team
      operationId: remove_team_project
      description: Revoke a team's access to a project. Requires org admin plus `CanInviteUsers` on the project.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Project removed from team. Returns an empty object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmptyResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams/{team_id}/users:
    get:
      summary: List team users
      operationId: list_team_users
      description: List all users that belong to a team.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
      responses:
        "200":
          description: Team users listed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      $ref: "#/components/schemas/TeamUser"
                required:
                  - users
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/orgs/{org_id}/teams/{team_id}/users/{org_user_id}:
    post:
      summary: Add user to team
      operationId: add_team_user
      description: Add an organization user to a team. Idempotent. Requires org admin.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
        - $ref: "#/components/parameters/OrgUserId"
      responses:
        "200":
          description: User added to team. Returns an empty object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmptyResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Remove user from team
      operationId: remove_team_user
      description: Remove a user from a team. Requires org admin.
      tags: [Teams]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/OrgId"
        - $ref: "#/components/parameters/TeamId"
        - $ref: "#/components/parameters/OrgUserId"
      responses:
        "200":
          description: User removed from team. Returns an empty object.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmptyResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/invites:
    post:
      summary: Create org-less user invitation (Terraform)
      operationId: create_orgless_invite
      description: >-
        Top-level invite endpoint used by Terraform to pre-create a User row
        without attaching org membership. Requires authentication only. The body
        accepts only `email`. If the email already maps to a confirmed User, the
        response includes a compact invite summary with status `accepted`; no
        invite row is inserted. Otherwise the endpoint ensures a User row
        exists with `email_confirmed=false`, persists or reuses a sent invite
        row, and sends an account invitation email when a new or reactivated
        invite is created. Caller can then attach org membership separately via
        POST /orgs/{org_id}/users.
      tags: [Users]
      x-badges:
        - name: beta
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrglessUserInviteCreateRequest"
      responses:
        "200":
          description: Invitation processed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserInviteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/join/{invite_id}:
    post:
      summary: Accept organization invitation
      operationId: join_org
      description: >-
        Accept a pending invitation and add the user to the organization.
        This endpoint is public — it does not require authentication, only a
        valid invitation ID. If the invited email does not yet correspond to a
        registered user, a new user is created with a confirmed email. If the
        user has no password set, a reset-password token is returned so the
        client can prompt the user to set one. If the invitation included a
        team, the user is also added to that team.
      tags: [Users]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/InviteId"
      responses:
        "200":
          description: >-
            Invitation accepted. When the user had no password set, returns
            `{resetPasswordToken}`. When the user already had a password, the
            response body is empty.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JoinResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/projects/{project_id}:
    get:
      summary: Get project
      operationId: get_project
      description: Get project details.
      tags: [Projects]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Project details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update project
      operationId: update_project
      description: Update project settings.
      tags: [Projects]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectCreateRequest"
      responses:
        "200":
          description: Project updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete project
      operationId: delete_project
      description: Delete a project.
      tags: [Projects]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: Project deleted. Returns the deleted project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/projects/{project_id}/tokens:
    get:
      summary: List project tokens
      operationId: list_project_tokens
      description: Retrieve all tokens for a project.
      tags: [ProjectTokens]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: List of project tokens.
          content:
            application/json:
              schema:
                type: object
                required:
                  - tokens
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: "#/components/schemas/ProjectToken"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create project token
      operationId: create_project_token
      description: Create a new token for a project. The token string is generated server-side.
      tags: [ProjectTokens]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectTokenCreateRequest"
      responses:
        "200":
          description: Project token created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectTokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/projects/{project_id}/tokens/{token_id}:
    get:
      summary: Get project token
      operationId: get_project_token
      description: Retrieve a specific project token by ID.
      tags: [ProjectTokens]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TokenId"
      responses:
        "200":
          description: Project token details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectTokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update project token
      operationId: update_project_token
      description: Update a project token's name.
      tags: [ProjectTokens]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TokenId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProjectTokenUpdateRequest"
      responses:
        "200":
          description: Project token updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectTokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete project token
      operationId: delete_project_token
      description: Delete a project token.
      tags: [ProjectTokens]
      x-badges:
        - name: beta
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/TokenId"
      responses:
        "200":
          description: Project token deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectTokenResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/users/current:
    get:
      summary: Get current user
      x-badges:
        - name: beta
      operationId: get_current_user
      description: >-
        Retrieve the currently authenticated user and their accessible projects.
        If no valid authentication is provided, returns a guest user with an empty project list
        instead of a 401 error.
      tags: [Users]
      responses:
        "200":
          description: Current user and accessible projects.
          content:
            application/json:
              schema:
                type: object
                required:
                  - user
                  - projects
                properties:
                  user:
                    $ref: "#/components/schemas/User"
                  projects:
                    type: array
                    items:
                      $ref: "#/components/schemas/Project"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/projects/{project_id}/notification-channels:
    get:
      summary: List notification channels
      x-badges:
        - name: beta
      operationId: list_notification_channels
      description: Retrieve all notification channels for a project.
      tags: [NotificationChannels]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      responses:
        "200":
          description: List of notification channels.
          content:
            application/json:
              schema:
                type: object
                required:
                  - channels
                properties:
                  channels:
                    type: array
                    items:
                      $ref: "#/components/schemas/NotificationChannel"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Create notification channel
      x-badges:
        - name: beta
      operationId: create_notification_channel
      description: Create a new notification channel.
      tags: [NotificationChannels]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationChannelRequest"
      responses:
        "200":
          description: Notification channel created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationChannelResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

  /internal/v1/projects/{project_id}/notification-channels/{channel_id}:
    get:
      summary: Get notification channel
      x-badges:
        - name: beta
      operationId: get_notification_channel
      description: Retrieve a specific notification channel by ID.
      tags: [NotificationChannels]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/ChannelId"
      responses:
        "200":
          description: Notification channel details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationChannelResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    put:
      summary: Update notification channel
      x-badges:
        - name: beta
      operationId: update_notification_channel
      description: Update an existing notification channel.
      tags: [NotificationChannels]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/ChannelId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationChannelRequest"
      responses:
        "200":
          description: Notification channel updated.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationChannelResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      summary: Delete notification channel
      x-badges:
        - name: beta
      operationId: delete_notification_channel
      description: Delete an existing notification channel.
      tags: [NotificationChannels]
      parameters:
        - $ref: "#/components/parameters/ProjectId"
        - $ref: "#/components/parameters/ChannelId"
      responses:
        "200":
          description: Notification channel deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationChannelResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    authTokenQuery:
      type: apiKey
      in: query
      name: auth_token
    uptraceDSNHeader:
      type: apiKey
      in: header
      name: uptrace-dsn
    dsnQuery:
      type: apiKey
      in: query
      name: dsn

  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Uptrace project ID.
      schema:
        type: integer
        format: uint32
    OrgId:
      name: org_id
      in: path
      required: true
      description: Organization ID.
      schema:
        type: integer
        format: uint64
    AlertId:
      name: alert_id
      in: path
      required: true
      description: Alert ID.
      schema:
        type: integer
        format: uint64
    MonitorId:
      name: monitor_id
      in: path
      required: true
      description: Monitor ID.
      schema:
        type: integer
        format: int64
    TimeStart:
      name: time_start
      in: query
      required: true
      description: Start time (inclusive) as RFC3339 timestamp.
      schema:
        type: string
        format: date-time
        description: RFC3339 timestamp.
      example: 2023-07-10T00:00:00Z
    TimeEnd:
      name: time_end
      in: query
      required: true
      description: End time (exclusive) as RFC3339 timestamp.
      schema:
        type: string
        format: date-time
        description: RFC3339 timestamp.
      example: 2023-07-11T00:00:00Z
    Limit:
      name: limit
      in: query
      required: false
      description: Limit number of results.
      schema:
        type: integer
        format: int32
      example: 10000
    DashboardId:
      name: dashboard_id
      in: path
      required: true
      description: Dashboard ID.
      schema:
        type: integer
        format: int64
    TokenId:
      name: token_id
      in: path
      required: true
      description: Project token ID.
      schema:
        type: integer
        format: uint64
    ChannelId:
      name: channel_id
      in: path
      required: true
      description: Notification channel ID.
      schema:
        type: integer
        format: int64
    TeamId:
      name: team_id
      in: path
      required: true
      description: Team ID.
      schema:
        type: integer
        format: uint64
    OrgUserId:
      name: org_user_id
      in: path
      required: true
      description: Organization user ID.
      schema:
        type: integer
        format: uint64
    InviteId:
      name: invite_id
      in: path
      required: true
      description: Invitation ID (32-character hex string).
      schema:
        type: string
        pattern: "^[0-9a-f]{32}$"
    GridItemId:
      name: grid_item_id
      in: path
      required: true
      description: Grid item ID.
      schema:
        type: integer
        format: int64
    SectionId:
      name: section_id
      in: path
      required: true
      description: Grid section ID.
      schema:
        type: integer
        format: int64

  schemas:
    AlertType:
      type: string
      description: Alert type.
      enum: [error, metric]

    AlertStatus:
      type: string
      description: Alert resolution status.
      enum: [unresolved, resolved, archived]

    AlertActivityState:
      type: string
      description: Alert activity state while unresolved.
      enum: [new, ongoing, escalated, regressed]

    AlertPriority:
      type: string
      description: Alert priority level.
      enum: [info, low, medium, high]

    Alert:
      type: object
      description: An alert fired by a monitor.
      properties:
        id:
          type: integer
          format: uint64
        projectId:
          type: integer
          format: uint32
        monitorId:
          type: integer
          format: uint64
        name:
          type: string
        type:
          $ref: "#/components/schemas/AlertType"
        attrs:
          type: object
          additionalProperties: true
          description: Custom alert attributes.
        event:
          $ref: "#/components/schemas/AlertEvent"
        createdAt:
          type: integer
          format: int64
          description: Unix timestamp (nanoseconds).
        updatedAt:
          type: integer
          format: int64
          description: Unix timestamp (nanoseconds).
      required:
        - id
        - projectId
        - name
        - type

    AlertEvent:
      type: object
      description: A lifecycle event on an alert.
      properties:
        id:
          type: integer
          format: uint64
        alertId:
          type: integer
          format: uint64
        name:
          type: string
          description: "Event type: created, status_changed, state_changed, priority_changed, recurring, note, assigned."
        status:
          $ref: "#/components/schemas/AlertStatus"
        activityState:
          $ref: "#/components/schemas/AlertActivityState"
        priority:
          $ref: "#/components/schemas/AlertPriority"
        createdAt:
          type: integer
          format: int64
          description: Unix timestamp (nanoseconds).
      required:
        - id
        - alertId
        - name
        - status

    User:
      type: object
      description: A registered user account.
      properties:
        id:
          type: integer
          format: uint64
        name:
          type: string
        email:
          type: string
        emailConfirmed:
          type: boolean
        avatar:
          type: string
          description: URL to the user's avatar image.
        emailsMutedUntil:
          type: number
          description: Unix timestamp in nanoseconds until emails are muted.
        otpEnabled:
          type: boolean
          description: Whether two-factor authentication is enabled.
        lastReadAt:
          type: number
          description: Unix timestamp in nanoseconds of last read.
        timeZone:
          type: string
          description: IANA time zone (e.g., America/New_York).
        createdAt:
          type: number
          description: Unix timestamp in nanoseconds.
      required:
        - id
        - name
        - email

    PublicUser:
      type: object
      description: Safe user summary for UI display.
      properties:
        id:
          type: integer
          format: uint64
        name:
          type: string
        avatar:
          type: string
          description: URL to the user's avatar image.
      required:
        - id
        - name
        - avatar

    Org:
      type: object
      description: An organization.
      properties:
        id:
          type: integer
          format: uint64
        name:
          type: string
        status:
          type: string
        budget:
          type: number
          format: double
        mfaRequired:
          type: boolean
        dynamicSampling:
          type: boolean
        createdAt:
          type: number
          description: Unix timestamp in nanoseconds.
        updatedAt:
          type: number
          description: Unix timestamp in nanoseconds.
      required:
        - id
        - name

    OrgResponse:
      type: object
      properties:
        org:
          $ref: "#/components/schemas/Org"
      required:
        - org

    OrgCreateRequest:
      type: object
      properties:
        name:
          type: string
        budget:
          type: number
          format: double
          description: Organization budget. Defaults to the system default if not set.
      required:
        - name

    OrgUpdateRequest:
      type: object
      properties:
        name:
          type: string
        mfaRequired:
          type: boolean
      required:
        - name

    OrgUpdateBudgetRequest:
      type: object
      properties:
        budget:
          type: number
          format: double
          description: Organization budget. Values below the minimum are clamped.
      required:
        - budget

    Project:
      type: object
      description: A project within an organization.
      properties:
        id:
          type: integer
          format: uint32
        orgId:
          type: integer
          format: uint64
        name:
          type: string
        samplingMode:
          type: string
          enum: [org_wide, dynamic, disabled, fixed]
        samplingFraction:
          type: number
          format: float64
        samplingBudget:
          type: number
          format: float64
        suspended:
          type: boolean
        groupByEnv:
          type: boolean
        groupFuncsByService:
          type: boolean
        semconvVersion:
          type: string
          enum: [none, "v1.25.0", "v1.33.0"]
        displayLogSeverity:
          type: boolean
        countDistinct:
          type: boolean
        spanQuery:
          type: string
        spanColumns:
          type: array
          items:
            type: string
        spanTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        spanRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        logQuery:
          type: string
        logColumns:
          type: array
          items:
            type: string
        logTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        logRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        eventQuery:
          type: string
        eventColumns:
          type: array
          items:
            type: string
        eventTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        eventRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        traceQuery:
          type: string
        traceColumns:
          type: array
          items:
            type: string
        traceTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        metricRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        createdAt:
          type: number
          description: Unix timestamp in nanoseconds.
        updatedAt:
          type: number
          description: Unix timestamp in nanoseconds.
        orgName:
          type: string
        mfaRequired:
          type: boolean
      required:
        - id
        - name

    ProjectResponse:
      type: object
      properties:
        project:
          $ref: "#/components/schemas/Project"
      required:
        - project

    ProjectCreateRequest:
      type: object
      properties:
        name:
          type: string
        groupByEnv:
          type: boolean
        groupFuncsByService:
          type: boolean
        semconvVersion:
          type: string
          enum: [none, "v1.25.0", "v1.33.0"]
        displayLogSeverity:
          type: boolean
        countDistinct:
          type: boolean
        spanTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        logTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        eventTimeRange:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 86400000 for 24h).
        spanRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        logRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        eventRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
        metricRetention:
          type: number
          format: double
          description: Duration in milliseconds (e.g., 2592000000 for 720h).
      required:
        - name

    SortDirection:
      type: string
      enum: [ASC, DESC]
      description: Sort direction.

    OrderItem:
      type: object
      properties:
        key:
          type: string
        order:
          type: string
          enum: [asc, desc]

    Span:
      type: object
      description: A span representing an individual operation in a distributed trace.
      properties:
        id:
          type: string
          description: Span ID (hex-encoded).
        parentId:
          type: string
          description: Parent span ID (hex-encoded, empty for root spans).
        traceId:
          type: string
          description: Trace ID (hex-encoded).
        standalone:
          type: boolean
          description: Whether the span is standalone (not part of a trace).
        projectId:
          type: integer
          format: uint32
          description: Project ID.
        groupId:
          type: string
          description: Group ID (uint64 as string).
        type:
          type: string
          description: Telemetry type (spans, events, logs, funcs).
        system:
          type: string
          description: Detected system (e.g., httpserver:all, db:postgresql, log:error).
        kind:
          type: string
          description: Span kind (client, server, producer, consumer, internal).
        name:
          type: string
          description: Span name.
        eventName:
          type: string
          description: Event name (for event spans).
        displayName:
          type: string
          description: Human-readable operation summary.
        time:
          type: number
          format: double
          description: Span start time as unix milliseconds.
        duration:
          type: number
          format: double
          description: Duration in milliseconds.
        statusCode:
          type: string
          description: Status code (ok, error, unset).
        statusMessage:
          type: string
          description: Optional status message.
        attrs:
          type: object
          additionalProperties: true
          description: Span attributes as key-value map. Keys include type suffix (e.g., service_name::str, count::int).
        events:
          type: array
          description: Child event spans.
          items:
            type: object
            additionalProperties: true
        logs:
          type: array
          description: Child log spans.
          items:
            type: object
            additionalProperties: true
        links:
          type: array
          description: Span links to other spans.
          items:
            type: object
            properties:
              traceId:
                type: string
              spanId:
                type: string
              attrs:
                type: object
                additionalProperties: true
      required: [id, traceId, name, time, duration, statusCode]

    GroupsResult:
      type: object
      description: Result of a span group aggregation query.
      properties:
        groups:
          type: array
          description: Array of group rows with dynamic columns. Each row may contain __hash, __name, __query metadata fields.
          items:
            type: object
            additionalProperties: true
        columns:
          type: array
          description: Column definitions describing the result schema.
          items:
            $ref: "#/components/schemas/QueryColumn"
        hasMore:
          type: boolean
          description: Whether more results exist beyond the limit.
        query:
          type: array
          description: Parsed query parts with error state.
          items:
            type: object
            additionalProperties: true
        join:
          type: array
          description: >-
            Parsed sub-query clauses for each joined alias. Populated only by
            list_trace_groups (one entry per non-root sub-query); empty for span groups.
          items:
            type: array
            items:
              type: object
              additionalProperties: true
        sorting:
          type: array
          description: Applied sorting configuration.
          items:
            $ref: '#/components/schemas/OrderItem'
        search:
          type: array
          description: Applied search matchers.
          items:
            type: object
            additionalProperties: true
        whereAttrs:
          type: object
          description: Map of WHERE attribute names to their matched values.
          additionalProperties: true
      required: [groups]

    QueryColumn:
      type: object
      description: Column definition for a query result.
      properties:
        name:
          type: string
          description: Column name.
        expr:
          type: string
          description: Column expression as written in the query.
        unit:
          type: string
          description: Column unit (e.g., ms, bytes).
        isNum:
          type: boolean
          description: Whether the column contains numeric values.
        isAgg:
          type: boolean
          description: Whether the column is an aggregation.
        isGroup:
          type: boolean
          description: Whether the column is a GROUP BY key.
        aggFunc:
          type: string
          description: Aggregation function applied to this column.
      required: [name]

    Timeseries:
      type: object
      description: A named timeseries with values aligned to timestamps.
      properties:
        name:
          type: string
          description: Metric name (e.g., countPerMin, errorCountPerMin, durationP50, durationP90, durationP99).
        value:
          type: array
          description: Array of metric values.
          items:
            type: number
            format: double
        time:
          type: array
          description: Array of timestamps as unix milliseconds.
          items:
            type: number
            format: double
      required: [name, value, time]

    AnnotationCreateRequest:
      type: object
      properties:
        name:
          type: string
          description: Annotation name.
        description:
          type: string
          description: Annotation description text in Markdown format.
        color:
          type: string
          description: Color to be used in charts.
        attrs:
          type: object
          additionalProperties:
            type: string
          description: Key-value metadata.
        fingerprint:
          type: string
          description: Unique string used for deduplication.
        time:
          type: string
          format: date-time
          description: Overrides annotation time in RFC3339 format.
        closeAlerts:
          $ref: "#/components/schemas/CloseAlertsParams"
      required:
        - name

    CloseAlertsParams:
      type: object
      description: >-
        Optional directive to close matching open alerts when the annotation is created.
        Alerts whose type matches and whose attributes contain all key/value pairs in attrs are closed.
      properties:
        type:
          type: string
          description: Alert type to close.
          enum: [error, metric]
        attrs:
          type: object
          additionalProperties:
            type: string
          description: Attribute key/value pairs that an alert must match to be closed.
      required:
        - type
    ListDashboardTemplatesResponse:
      type: object
      properties:
        templates:
          type: array
          items:
            $ref: "#/components/schemas/DashboardTemplateItem"
        tags:
          type: array
          items:
            type: string
          description: All unique tags across templates.
        statuses:
          type: array
          description: Per-status counts across the project (ignores the status filter).
          items:
            $ref: "#/components/schemas/TemplateStatusCount"
      required: [templates]

    TemplateStatusCount:
      type: object
      description: Number of dashboard templates in a given status.
      properties:
        status:
          $ref: "#/components/schemas/TemplateStatus"
        count:
          type: integer
      required: [status, count]

    DashboardTemplateItem:
      type: object
      properties:
        id:
          type: string
          description: Template ID.
        name:
          type: string
          description: Template name.
        description:
          type: string
          description: Template description.
        tags:
          type: array
          items:
            type: string
          description: Template tags.
        version:
          type: string
          description: Template version.
        setupLink:
          type: string
          description: Link to setup instructions.
        docLink:
          type: string
          description: Link to documentation.
        status:
          $ref: "#/components/schemas/TemplateStatus"
        dashboardId:
          type: integer
          description: ID of the dashboard created from this template, if any.
      required: [id, name, description, status]

    GetDashboardTemplateResponse:
      type: object
      properties:
        template:
          $ref: "#/components/schemas/DashboardTemplate"
        yaml:
          type: string
          description: Full dashboard template YAML definition.
      required: [template]

    DashboardTemplate:
      type: object
      properties:
        id:
          type: string
          description: Template ID.
        name:
          type: string
          description: Template name.
        description:
          type: string
          description: Template description.
        tags:
          type: array
          items:
            type: string
          description: Template tags.
        version:
          type: string
          description: Template version.
        setupLink:
          type: string
          description: Link to setup instructions.
        docLink:
          type: string
          description: Link to documentation.
        status:
          $ref: "#/components/schemas/TemplateStatus"
        dashboardId:
          type: integer
          description: ID of the dashboard created from this template, if any.
        yaml:
          type: string
          description: Template YAML definition.
      required: [id, name, description, status]

    TemplateStatus:
      type: string
      description: Template availability status.
      enum: [ready, pending, installed]

    ExploreMetricsResponse:
      type: object
      properties:
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/ExploredMetric"
        hasMore:
          type: boolean
        search:
          type: array
          description: Parsed search matchers applied to the request.
          items:
            type: object
            additionalProperties: true
      required: [metrics, hasMore]

    ExploredMetric:
      type: object
      properties:
        name:
          type: string
          description: Metric name.
        instrument:
          type: string
          enum: [histogram, summary, counter, prom-counter, gauge, additive]
          description: OpenTelemetry instrument type.
        unit:
          type: string
          description: Metric unit.
        description:
          type: string
          description: Metric description.
        attrKeys:
          type: array
          items:
            type: string
          description: Available attribute keys for grouping.
        libraryName:
          type: string
          description: Instrumentation library name.
        libraryVersion:
          type: string
          description: Instrumentation library version.
        numTimeseries:
          type: integer
          description: Number of active timeseries.
      required: [name, instrument]

    ListMetricAttributesResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/MetricAttributeKey"
        noData:
          type: boolean
          description: True when no attributes exist for the project at all (independent of the current filter).
        whereAttrs:
          type: object
          description: Map of WHERE attribute names to their matched values, derived from the parsed query.
          additionalProperties: true
      required: [items]

    MetricAttributeKey:
      type: object
      properties:
        value:
          type: string
          description: "Attribute key with type suffix (e.g. host_name::str)."
        title:
          type: string
          description: Display label for the attribute. When empty, clients fall back to value.
        subtitle:
          type: string
          description: Additional context shown below the title in autocomplete lists.
        kind:
          type: string
          description: "Attribute type: str, int, float."
        unit:
          type: string
          description: Attribute unit.
        pinned:
          type: boolean
          description: Whether the attribute is pinned.
        count:
          type: integer
          description: Number of metrics using this attribute.
      required: [value, kind]

    ListMetricAttributeValuesResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/MetricAttributeValue"
        hasMore:
          type: boolean
      required: [items, hasMore]

    MetricAttributeValue:
      type: object
      properties:
        value:
          type: string
          description: Attribute value.
        count:
          type: integer
          description: Occurrence count.
      required: [value, count]

    ErrorDetail:
      type: object
      description: Machine-readable error code and human-readable message.
      properties:
        code:
          type: string
          description: Short error code such as forbidden or not_found, or a field name for validation errors.
        message:
          type: string
          description: Human-readable explanation of what went wrong.
      required: [code, message]

    Error:
      type: object
      description: Uniform error envelope returned for every non-2xx response.
      properties:
        error:
          $ref: "#/components/schemas/ErrorDetail"
        statusCode:
          type: integer
          description: HTTP status code; mirrors the response status line.
        traceId:
          type: string
          description: Server-assigned trace ID useful for correlating logs.
      required: [error, statusCode]

    SeedDataRequest:
      type: object
      description: Fixture data used to create, update, or delete resources by key.
      properties:
        users:
          type: array
          items:
            $ref: "#/components/schemas/UserFixture"
        userTokens:
          type: array
          items:
            $ref: "#/components/schemas/UserTokenFixture"
        orgs:
          type: array
          items:
            $ref: "#/components/schemas/OrgFixture"
        orgUsers:
          type: array
          items:
            $ref: "#/components/schemas/OrgUserFixture"
        projects:
          type: array
          items:
            $ref: "#/components/schemas/ProjectFixture"
        projectTokens:
          type: array
          items:
            $ref: "#/components/schemas/ProjectTokenFixture"
        projectUsers:
          type: array
          items:
            $ref: "#/components/schemas/ProjectUserFixture"

    LinkDataRequest:
      type: object
      description: Links existing resources to fixture keys.
      properties:
        orgs:
          type: array
          items:
            $ref: "#/components/schemas/LinkFixture"
        orgUsers:
          type: array
          items:
            $ref: "#/components/schemas/LinkFixture"
        projects:
          type: array
          items:
            $ref: "#/components/schemas/LinkFixture"
        projectTokens:
          type: array
          items:
            $ref: "#/components/schemas/LinkFixture"

    UserFixture:
      type: object
      properties:
        key:
          type: string
          description: Unique fixture key.
        name:
          type: string
          description: Display name.
        email:
          type: string
          format: email
        password:
          type: string
          description: Plain-text password (hashed by Uptrace).
      required:
        - key
        - name
        - email
        - password

    UserTokenFixture:
      type: object
      properties:
        key:
          type: string
        userKey:
          type: string
          description: References users.key.
        token:
          type: string
        name:
          type: string
          description: Human-readable token name.
      required:
        - key
        - userKey
        - token

    OrgFixture:
      type: object
      properties:
        key:
          type: string
        name:
          type: string
        description:
          type: string
      required:
        - key
        - name

    OrgUserFixture:
      type: object
      properties:
        key:
          type: string
        orgKey:
          type: string
          description: References orgs.key.
        userKey:
          type: string
          description: References users.key.
        role:
          type: string
          enum:
            - owner
            - admin
            - viewer
            - member
            - collaborator
            - billing_manager
      required:
        - key
        - orgKey
        - userKey
        - role

    ProjectFixture:
      type: object
      properties:
        key:
          type: string
        name:
          type: string
        orgKey:
          type: string
          description: References orgs.key.
        description:
          type: string
      required:
        - key
        - name
        - orgKey

    ProjectTokenFixture:
      type: object
      properties:
        key:
          type: string
        projectKey:
          type: string
          description: References projects.key.
        token:
          type: string
        name:
          type: string
      required:
        - key
        - projectKey
        - token

    ProjectUserFixture:
      type: object
      properties:
        key:
          type: string
        projectKey:
          type: string
          description: References projects.key.
        orgUserKey:
          type: string
          description: References orgUsers.key.
        permLevel:
          type: string
          enum:
            - admin
            - maintain
            - view
            - none
      required:
        - key
        - projectKey
        - orgUserKey
        - permLevel

    LinkFixture:
      type: object
      properties:
        id:
          type: integer
          format: int64
        key:
          type: string
      required:
        - id
        - key

    MonitorResponse:
      type: object
      properties:
        monitor:
          $ref: "#/components/schemas/Monitor"
      required:
        - monitor
    MonitorStatusCount:
      type: object
      description: Number of monitors in a given status.
      properties:
        status:
          type: string
        count:
          type: integer
      required: [status, count]

    BaseMonitorRequest:
      type: object
      properties:
        name:
          type: string
        notifyEveryoneByEmail:
          type: boolean
        teamIds:
          type: array
          items:
            type: integer
        channelIds:
          type: array
          items:
            type: integer
        repeatInterval:
          $ref: "#/components/schemas/RepeatInterval"
        trendAggFunc:
          type: string
          description: Aggregation function used to compute the trend baseline.
          enum: [sum, avg, median, last]
        trendSensitivity:
          type: string
          description: Sensitivity level of the trend-based anomaly detector.
          enum: [low, medium, high]
      required:
        - name
    MetricMonitorRequest:
      allOf:
        - $ref: "#/components/schemas/BaseMonitorRequest"
        - type: object
          properties:
            type:
              type: string
              enum:
                - metric
            params:
              $ref: "#/components/schemas/MetricMonitorParams"
          required:
            - type
            - params
    ErrorMonitorRequest:
      allOf:
        - $ref: "#/components/schemas/BaseMonitorRequest"
        - type: object
          properties:
            type:
              type: string
              enum:
                - error
            params:
              $ref: "#/components/schemas/ErrorMonitorParams"
          required:
            - type
            - params
    MetricMonitorParams:
      type: object
      properties:
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/MonitorMetric"
        query:
          type: string
        column:
          $ref: "#/components/schemas/Column"
        resolution:
          type: number
          description: Resolution in milliseconds.
        numEvalPoints:
          type: integer
        absentPoints:
          type: string
          enum:
            - zero
            - ignore
            - alert
        timeOffset:
          type: number
          description: Time offset in milliseconds.
        detector:
          $ref: "#/components/schemas/DetectorConfig"
      required:
        - metrics
        - query
        - detector
    DetectorConfig:
      type: object
      properties:
        type:
          type: string
          enum:
            - manual
            - auto
        params:
          oneOf:
            - $ref: "#/components/schemas/ManualDetectorParams"
            - $ref: "#/components/schemas/AutoDetectorParams"
      required:
        - type
        - params
    ManualDetectorParams:
      type: object
      properties:
        minValue:
          type: number
        maxValue:
          type: number
        recovery:
          $ref: "#/components/schemas/Recovery"
    AutoDetectorParams:
      type: object
      properties:
        tolerance:
          type: string
          enum:
            - low
            - medium
            - high
        trainingPeriod:
          type: number
          description: Training period in milliseconds.
        minDevFraction:
          type: number
        minDevAbsolute:
          type: number
    ErrorMonitorParams:
      type: object
      properties:
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/MonitorMetric"
        query:
          type: string
      required:
        - metrics
        - query
    Column:
      type: object
      properties:
        name:
          type: string
          description: Name of the query result column being monitored.
        unit:
          type: string
          description: Display unit for the column values (e.g. "milliseconds", "bytes").
    Recovery:
      type: object
      properties:
        minValue:
          type: number
        maxValue:
          type: number
    MonitorMetric:
      type: object
      properties:
        name:
          type: string
        alias:
          type: string
      required:
        - name

    RepeatInterval:
      type: object
      description: Monitor repeat interval configuration.
      oneOf:
        - $ref: "#/components/schemas/DefaultRepeatInterval"
        - $ref: "#/components/schemas/FixedRepeatInterval"
        - $ref: "#/components/schemas/LinearRepeatInterval"
        - $ref: "#/components/schemas/ExponentialRepeatInterval"

    DefaultRepeatInterval:
      type: object
      properties:
        strategy:
          type: string
          enum: [default]

    FixedRepeatInterval:
      type: object
      properties:
        strategy:
          type: string
          enum: [fixed]
        interval:
          type: number
          description: Fixed interval in milliseconds.
      required:
        - strategy
        - interval

    LinearRepeatInterval:
      type: object
      properties:
        strategy:
          type: string
          enum: [linear]
        min:
          type: number
          description: Minimum interval in milliseconds.
        max:
          type: number
          description: Maximum interval in milliseconds.
      required:
        - strategy
        - min
        - max

    ExponentialRepeatInterval:
      type: object
      properties:
        strategy:
          type: string
          enum: [exponential]
        min:
          type: number
          description: Minimum interval in milliseconds.
        max:
          type: number
          description: Maximum interval in milliseconds.
      required:
        - strategy
        - min
        - max

    Monitor:
      type: object
      properties:
        id:
          type: integer
          format: int64
        projectId:
          type: integer
          format: int64
        key:
          type: string
          description: >-
            Deduplication key set when the monitor is created from a YAML dashboard template.
            Empty for monitors created via the UI. When non-empty, (project_id, key) is unique.
        name:
          type: string
        isNameTemplated:
          type: boolean
          description: True when the name contains template placeholders that are expanded at evaluation time.
        type:
          type: string
          enum: [metric, error]
        status:
          type: string
          enum: [active, paused, firing, no_data, disabled]
        pausedUntil:
          type: number
          description: Unix timestamp (nanoseconds) until which the monitor is paused. Set when status=paused.
        error:
          type: string
          description: Last error message that caused the monitor to be disabled. Set when status=disabled.
        notifyEveryoneByEmail:
          type: boolean
        teamIds:
          type: array
          items:
            type: integer
        channelIds:
          type: array
          items:
            type: integer
        repeatInterval:
          $ref: "#/components/schemas/RepeatInterval"
        params:
          type: object
          additionalProperties: true
        trendAggFunc:
          type: string
          description: Aggregation function used to compute the trend baseline.
          enum: [sum, avg, median, last]
        trendSensitivity:
          type: string
          description: Sensitivity level of the trend-based anomaly detector.
          enum: [low, medium, high]
        trendUpdatedAt:
          type: number
          description: Unix timestamp (nanoseconds) when the trend baseline was last refreshed.
        createdAt:
          type: number
        updatedAt:
          type: number
        checkedAt:
          type: number
          description: Unix timestamp (nanoseconds) when the monitor was last evaluated.
        nextCheckTime:
          type: number
          description: Unix timestamp (nanoseconds) when the monitor is scheduled to be evaluated next.
      required:
        - id
        - name
        - type
        - status

    Dashboard:
      type: object
      properties:
        id:
          type: integer
          format: int64
        projectId:
          type: integer
          format: int64
        templateId:
          type: string
          description: ID of the dashboard template this dashboard was created from, if any.
        name:
          type: string
        version:
          type: string
          description: Dashboard schema version, set when the dashboard is created from a template.
        pinned:
          type: boolean
        tags:
          type: array
          description: Tags used to categorize and filter dashboards.
          items:
            type: string
        sortingOrder:
          type: integer
          format: int64
          description: Manual sort order applied when listing dashboards.
        minInterval:
          type: number
          description: Minimum query interval in nanoseconds.
        timeOffset:
          type: number
          description: Default time offset applied to queries in nanoseconds.
        tooltipsConnected:
          type: boolean
          description: When true, hovering a chart highlights the same timestamp on all charts in the dashboard.
        defaultTimePeriod:
          type: number
          description: Default time period in nanoseconds applied when the dashboard is opened without an explicit range.
        table:
          $ref: "#/components/schemas/TableGridItemParams"
        gridMaxWidth:
          type: integer
        gridQuery:
          type: string
        gridVariables:
          type: array
          description: >-
            Attribute names used as filter variables in the Grid tab. When empty, the
            grouping attributes from the table are used instead.
          items:
            type: string
        createdAt:
          type: number
        updatedAt:
          type: number
      required:
        - id
        - projectId
        - name

    TableGridItemParams:
      type: object
      description: Configuration for the Table tab of a dashboard.
      properties:
        metrics:
          type: array
          items:
            $ref: "#/components/schemas/MetricAlias"
        query:
          type: string
        groupingAttrs:
          type: array
          description: Grouping attributes extracted from the query.
          items:
            type: string
        overrides:
          type: array
          description: Per-column visual overrides.
          items:
            type: object
            additionalProperties: true
        variables:
          type: array
          items:
            type: string
        itemsPerPage:
          type: integer
        compactTable:
          type: boolean
        linesPerRow:
          type: integer
        nullsMode:
          type: string
          description: How null values are rendered in the table.
        sortBy:
          type: array
          description: Column sort order applied to the table.
          items:
            type: object
            additionalProperties: true

    DashboardResponse:
      type: object
      properties:
        dashboard:
          $ref: "#/components/schemas/Dashboard"
      required:
        - dashboard

    DashboardTableUpdate:
      type: object
      properties:
        name:
          type: string
        tableMetrics:
          type: array
          items:
            $ref: "#/components/schemas/MetricAlias"
        tableQuery:
          type: string
        tableColumnMap:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/TableColumn"

    MetricAlias:
      type: object
      properties:
        name:
          type: string
        alias:
          type: string
      required:
        - name
        - alias

    TableColumn:
      type: object
      properties:
        unit:
          type: string
        color:
          type: string
        aggFunc:
          type: string
          enum: [min, max, sum, avg, avg_zero, median, last]
        sparklineDisabled:
          type: boolean

    GridSection:
      type: object
      properties:
        id:
          type: integer
          format: int64
        dashboardId:
          type: integer
          format: int64
        title:
          type: string
        description:
          type: string
        expanded:
          type: boolean
        index:
          type: integer
        items:
          type: array
          items:
            $ref: "#/components/schemas/GridItem"
        createdAt:
          type: number
        updatedAt:
          type: number
      required:
        - id
        - dashboardId
        - title
        - index

    GridSectionRequest:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        expanded:
          type: boolean
      required:
        - title

    GridSectionResponse:
      type: object
      properties:
        gridSection:
          $ref: "#/components/schemas/GridSection"
      required:
        - gridSection

    GridItem:
      type: object
      properties:
        id:
          type: integer
          format: int64
        title:
          type: string
        description:
          type: string
        dashboardId:
          type: integer
          format: int64
        dashboardKind:
          type: string
          enum: [grid, table]
        sectionId:
          type: integer
          format: int64
        width:
          type: integer
        height:
          type: integer
        xAxis:
          type: integer
        yAxis:
          type: integer
        type:
          type: string
          enum: [chart, table, heatmap, gauge]
        params:
          type: object
          additionalProperties: true
        createdAt:
          type: number
        updatedAt:
          type: number
      required:
        - id
        - title
        - dashboardId
        - dashboardKind
        - type

    GridItemRequest:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        dashboardKind:
          type: string
          enum: [grid, table]
        sectionId:
          type: integer
          format: int64
        type:
          type: string
          enum: [chart, table, heatmap, gauge]
        params:
          type: object
          additionalProperties: true
      required:
        - title
        - dashboardKind
        - sectionId
        - type

    GridItemResponse:
      type: object
      properties:
        gridItem:
          $ref: "#/components/schemas/GridItem"
      required:
        - gridItem

    SlackParams:
      type: object
      description: Slack params. When authMethod=webhook, webhookUrl is required. When authMethod=token, token and channel are required.
      properties:
        webhookUrl:
          type: string
        token:
          type: string
        channel:
          type: string
        authMethod:
          type: string
          enum: [webhook, token]

    GoogleChatParams:
      type: object
      properties:
        webhookUrl:
          type: string
      required:
        - webhookUrl

    MattermostParams:
      type: object
      properties:
        webhookUrl:
          type: string
      required:
        - webhookUrl

    PagerdutyParams:
      type: object
      properties:
        routingKey:
          type: string
        severity:
          type: string
          enum: [critical, error, warning, info]
      required:
        - routingKey
        - severity

    ServicenowParams:
      type: object
      properties:
        url:
          type: string
        username:
          type: string
        password:
          type: string
        category:
          type: string
        subcategory:
          type: string
        impact:
          type: string
          enum: ["1", "2", "3"]
        urgency:
          type: string
          enum: ["1", "2", "3"]
        severity:
          type: string
          enum: ["1", "2", "3", "4", "5"]
        callerId:
          type: string
        group:
          type: string
        assignedTo:
          type: string
        openedBy:
          type: string
        notify:
          type: string
          enum: ["1", "2"]
        dueDate:
          type: string
      required:
        - url
        - username
        - password

    OpsgenieParams:
      type: object
      properties:
        apiKey:
          type: string
        priority:
          type: string
          enum: [P1, P2, P3, P4, P5]
      required:
        - apiKey
        - priority

    TelegramParams:
      type: object
      properties:
        chatId:
          type: integer
          format: int64
      required:
        - chatId

    TeamsParams:
      type: object
      properties:
        webhookUrl:
          type: string
      required:
        - webhookUrl

    WebhookParams:
      type: object
      properties:
        url:
          type: string
        payload:
          type: object
          additionalProperties: true
      required:
        - url

    AlertmanagerParams:
      type: object
      description: Alertmanager notification channel params.
      properties:
        url:
          type: string
          description: Alertmanager API endpoint URL.
        authMethod:
          type: string
          enum: [none, basic_auth, bearer]
          description: Authentication method.
        username:
          type: string
          description: Username for basic_auth.
        password:
          type: string
          description: Password for basic_auth.
        token:
          type: string
          description: Token for bearer auth.
      required:
        - url

    PushoverParams:
      type: object
      description: Pushover notification channel params.
      properties:
        token:
          type: string
        userKey:
          type: string
        priority:
          type: integer
          description: Priority from -2 (lowest) to 2 (emergency).
        sound:
          type: string
      required:
        - token
        - userKey

    IncidentioParams:
      type: object
      description: incident.io notification channel params.
      properties:
        url:
          type: string
          description: Alert Events V2 endpoint URL.
        apiKey:
          type: string
          description: incident.io API key.
      required:
        - url
        - apiKey

    NotificationChannel:
      type: object
      properties:
        id:
          type: integer
          format: int64
        projectId:
          type: integer
          format: int64
        name:
          type: string
        type:
          type: string
          enum:
            [
              slack,
              google_chat,
              mattermost,
              pagerduty,
              servicenow,
              opsgenie,
              telegram,
              teams,
              pushover,
              webhook,
              alertmanager,
              incidentio,
            ]
        status:
          type: string
          enum: [draft, delivering, paused, disabled]
        error:
          type: string
        matchAll:
          type: boolean
        condition:
          type: string
        priorities:
          type: array
          items:
            type: string
            enum: [info, low, medium, high]
        params:
          oneOf:
            - $ref: "#/components/schemas/SlackParams"
            - $ref: "#/components/schemas/GoogleChatParams"
            - $ref: "#/components/schemas/MattermostParams"
            - $ref: "#/components/schemas/PagerdutyParams"
            - $ref: "#/components/schemas/ServicenowParams"
            - $ref: "#/components/schemas/OpsgenieParams"
            - $ref: "#/components/schemas/TelegramParams"
            - $ref: "#/components/schemas/TeamsParams"
            - $ref: "#/components/schemas/PushoverParams"
            - $ref: "#/components/schemas/WebhookParams"
            - $ref: "#/components/schemas/AlertmanagerParams"
            - $ref: "#/components/schemas/IncidentioParams"
        monitorIds:
          type: array
          items:
            type: integer
            format: int64
        monitorCount:
          type: integer
        sentCount:
          type: integer
        lastSentAt:
          type: string
          format: date-time
      required:
        - id
        - projectId
        - name
        - type
        - status
        - params

    NotificationChannelRequest:
      type: object
      properties:
        name:
          type: string
        type:
          type: string
          enum:
            [
              slack,
              google_chat,
              mattermost,
              pagerduty,
              servicenow,
              opsgenie,
              telegram,
              teams,
              pushover,
              webhook,
              alertmanager,
              incidentio,
            ]
        matchAll:
          type: boolean
        monitorIds:
          type: array
          items:
            type: integer
            format: int64
        condition:
          type: string
        priorities:
          type: array
          items:
            type: string
            enum: [info, low, medium, high]
        params:
          oneOf:
            - $ref: "#/components/schemas/SlackParams"
            - $ref: "#/components/schemas/GoogleChatParams"
            - $ref: "#/components/schemas/MattermostParams"
            - $ref: "#/components/schemas/PagerdutyParams"
            - $ref: "#/components/schemas/ServicenowParams"
            - $ref: "#/components/schemas/OpsgenieParams"
            - $ref: "#/components/schemas/TelegramParams"
            - $ref: "#/components/schemas/TeamsParams"
            - $ref: "#/components/schemas/PushoverParams"
            - $ref: "#/components/schemas/WebhookParams"
            - $ref: "#/components/schemas/AlertmanagerParams"
            - $ref: "#/components/schemas/IncidentioParams"
      required:
        - name
        - type
        - priorities
        - params

    NotificationChannelResponse:
      type: object
      properties:
        channel:
          $ref: "#/components/schemas/NotificationChannel"
      required:
        - channel

    ProjectToken:
      type: object
      description: A project authentication token with its computed DSN.
      properties:
        id:
          type: integer
          format: uint64
          description: Token ID.
        projectId:
          type: integer
          format: uint32
          description: Project ID this token belongs to.
        name:
          type: string
          description: Human-readable token name.
        token:
          type: string
          description: The secret token string.
        dsn:
          type: string
          description: Computed DSN (ingest URL with token embedded).
        createdAt:
          type: number
          format: float
          description: Unix timestamp in nanoseconds.
      required:
        - id
        - projectId
        - token

    ProjectTokenCreateRequest:
      type: object
      properties:
        name:
          type: string
          description: Human-readable token name.

    ProjectTokenUpdateRequest:
      type: object
      properties:
        name:
          type: string
          description: Human-readable token name.
      required:
        - name

    ProjectTokenResponse:
      type: object
      properties:
        token:
          $ref: "#/components/schemas/ProjectToken"
      required:
        - token

    PermLevel:
      type: string
      description: Project-level permission override (empty when unset).
      enum:
        - none
        - view
        - edit
        - admin

    Team:
      type: object
      description: A team groups users for project-scoped access control within an organization.
      properties:
        id:
          type: integer
          format: uint64
        orgId:
          type: integer
          format: uint64
        name:
          type: string
        permLevel:
          $ref: "#/components/schemas/PermLevel"
        createdAt:
          type: number
          description: Unix timestamp in milliseconds.
        numUser:
          type: integer
          format: uint64
          description: Number of users in the team. Populated only on list responses.
        numProject:
          type: integer
          format: uint64
          description: Number of projects the team has access to. Populated only on list responses.
      required:
        - id
        - orgId
        - name

    TeamResponse:
      type: object
      properties:
        team:
          $ref: "#/components/schemas/Team"
      required:
        - team

    TeamCreateRequest:
      type: object
      properties:
        name:
          type: string
        permLevel:
          $ref: "#/components/schemas/PermLevel"
      required:
        - name

    TeamUpdateRequest:
      type: object
      description: >-
        Omitting or sending an empty `name` preserves the existing name.
        Omitting `permLevel` clears it to null.
      properties:
        name:
          type: string
        permLevel:
          $ref: "#/components/schemas/PermLevel"

    TeamUser:
      type: object
      description: A user that belongs to a team.
      properties:
        id:
          type: integer
          format: uint64
          description: User ID.
        name:
          type: string
        email:
          type: string
        orgUserId:
          type: integer
          format: uint64
          description: OrgUser ID. Use this value to add or remove the user via team-user endpoints.
      required:
        - id
        - name
        - email
        - orgUserId

    TeamProjectAddRequest:
      type: object
      description: >-
        Request body for adding a project to a team. A non-empty JSON body is required
        so the server can decode it. The `permLevel` field is accepted but currently
        ignored by the backend.
      properties:
        permLevel:
          $ref: "#/components/schemas/PermLevel"

    UserRole:
      type: string
      description: A user's role within an organization.
      enum:
        - owner
        - admin
        - member
        - viewer
        - billing_manager
        - collaborator

    InviteStatus:
      type: string
      description: Lifecycle status of an organization invitation.
      enum:
        - sent
        - accepted
        - canceled
        - expired

    OrgUser:
      type: object
      description: An organization membership row. Links a user to an organization with a role.
      properties:
        id:
          type: integer
          format: uint64
        orgId:
          type: integer
          format: uint64
        userId:
          type: integer
          format: uint64
        providerId:
          type: integer
          format: uint64
          description: >-
            SSO provider ID if the membership was auto-provisioned via OIDC/SAML.
            Zero for memberships created directly or via invitation acceptance.
        role:
          $ref: "#/components/schemas/UserRole"
        updatedAt:
          type: number
          description: Unix timestamp in nanoseconds.
      required:
        - id
        - orgId
        - userId
        - role

    OrgUserDetail:
      type: object
      description: An OrgUser enriched with user profile and membership counts. Returned by list endpoints.
      allOf:
        - $ref: "#/components/schemas/OrgUser"
        - type: object
          properties:
            name:
              type: string
            email:
              type: string
            avatar:
              type: string
              description: URL to the user's avatar image.
            mfaEnabled:
              type: boolean
              description: Whether the user has two-factor authentication enabled.
            providerDomain:
              type: string
              description: SSO provider domain if the membership was auto-provisioned. Empty otherwise.
            numProject:
              type: integer
              format: int64
              description: Number of projects the user has access to within this organization.
            numTeam:
              type: integer
              format: int64
              description: Number of teams the user belongs to within this organization.
          required:
            - name
            - email

    OrgUserListResponse:
      type: object
      properties:
        users:
          type: array
          items:
            $ref: "#/components/schemas/OrgUserDetail"
      required:
        - users

    OrgUserResponse:
      type: object
      description: Response wrapping a single OrgUser under the `user` key.
      properties:
        user:
          $ref: "#/components/schemas/OrgUser"
      required:
        - user

    OrgUserShowResponse:
      type: object
      description: Detailed org user view returned by `GET /orgs/{org_id}/users/{org_user_id}`.
      properties:
        user:
          $ref: "#/components/schemas/User"
        orgUser:
          $ref: "#/components/schemas/OrgUser"
        projects:
          type: array
          description: Projects the user has access to in this organization.
          items:
            $ref: "#/components/schemas/Project"
        teams:
          type: array
          description: Teams the user belongs to in this organization.
          items:
            $ref: "#/components/schemas/Team"
      required:
        - user
        - orgUser
        - projects
        - teams

    OrgUserRoleUpdateRequest:
      type: object
      properties:
        role:
          $ref: "#/components/schemas/UserRole"
      required:
        - role

    OrgUserCreateRequest:
      type: object
      description: Body for POST /orgs/{org_id}/users. Creates an OrgUser membership for an existing User by ID. Idempotent — re-posting the same userId updates the role.
      properties:
        userId:
          type: integer
          format: uint64
          description: ID of an existing User to attach to the org.
        role:
          $ref: "#/components/schemas/UserRole"
      required:
        - userId
        - role

    OrgUserCreateResponse:
      type: object
      description: Response from POST /orgs/{org_id}/users. Wraps the created or updated OrgUser under `orgUser`.
      properties:
        orgUser:
          $ref: "#/components/schemas/OrgUser"
      required:
        - orgUser

    ResendInviteResponse:
      type: object
      description: Response from POST /orgs/{org_id}/invites/{invite_id}/resend.
      properties:
        ok:
          type: boolean
      required:
        - ok

    UserInviteCancelResponse:
      type: object
      description: Response from DELETE /orgs/{org_id}/invites/{invite_id}.
      properties:
        invite:
          $ref: "#/components/schemas/UserInvite"
      required:
        - invite

    OrgUserProject:
      type: object
      description: A user's project-level permission override.
      properties:
        orgId:
          type: integer
          format: uint64
        orgUserId:
          type: integer
          format: uint64
        projectId:
          type: integer
          format: uint32
        permLevel:
          $ref: "#/components/schemas/PermLevel"
      required:
        - orgId
        - orgUserId
        - projectId
        - permLevel

    OrgUserProjectResponse:
      type: object
      description: Response wrapping an OrgUserProject under the `user` key.
      properties:
        user:
          $ref: "#/components/schemas/OrgUserProject"
      required:
        - user

    OrgUserProjectUpdateRequest:
      type: object
      properties:
        permLevel:
          $ref: "#/components/schemas/PermLevel"
      required:
        - permLevel

    UserInvite:
      type: object
      description: A pending or resolved organization invitation.
      properties:
        id:
          type: string
          description: 32-character hex trace ID used as the invitation token in the /join URL.
        email:
          type: string
          description: Invitee email (lowercased, trimmed).
        orgId:
          type: integer
          format: uint64
          description: Target organization. Omitted for top-level (Terraform) invites that pre-create only the user row.
        role:
          $ref: "#/components/schemas/UserRole"
        teamIds:
          type: array
          items:
            type: integer
            format: uint64
          description: Teams the invitee is added to on acceptance. Each team must belong to the same org. Empty when no teams are specified.
        status:
          $ref: "#/components/schemas/InviteStatus"
        createdAt:
          type: number
          description: Unix timestamp in nanoseconds.
      required:
        - id
        - email
        - status
        - createdAt

    UserInviteResponse:
      type: object
      description: >-
        Response from creating or processing an invitation. `user` is a public
        invitee summary. `invite` is a compact invite summary; `invite.status` is
        `sent` for pending invitations and `accepted` when the email already maps
        to a confirmed user. `invite.id` is the persisted invite ID for pending
        invites and a response-only random ID for already-confirmed users.
        `invite.orgId` is present for org-scoped actions; when `invite.status` is
        `accepted` and `invite.orgId` is present, the confirmed user was attached
        to that org.
      properties:
        user:
          $ref: "#/components/schemas/PublicUser"
        invite:
          $ref: "#/components/schemas/InviteSummary"
      required:
        - user
        - invite

    InviteSummary:
      type: object
      description: Compact invite summary returned from create-invite endpoints.
      properties:
        id:
          type: string
          description: 32-character hex trace ID.
        status:
          $ref: "#/components/schemas/InviteStatus"
        orgId:
          type: integer
          format: uint64
          description: Present for org-scoped invite actions.
      required:
        - id
        - status

    UserInviteListResponse:
      type: object
      properties:
        invites:
          type: array
          items:
            $ref: "#/components/schemas/UserInvite"
      required:
        - invites

    UserInviteCreateRequest:
      type: object
      properties:
        email:
          type: string
          description: Email address of the invitee. Required; trimmed and lowercased server-side.
        role:
          $ref: "#/components/schemas/UserRole"
        teamIds:
          type: array
          items:
            type: integer
            format: uint64
          description: Teams to add the invitee to on acceptance. Each team must belong to the URL org.
      required:
        - email

    OrglessUserInviteCreateRequest:
      type: object
      description: Body for the top-level POST /invites (Terraform path). Only `email` is accepted; `role` and `teamIds` would be meaningless without an org.
      properties:
        email:
          type: string
          description: Email address of the invitee. Required; trimmed and lowercased server-side.
      required:
        - email

    JoinResponse:
      type: object
      description: >-
        Response from accepting an invitation. Contains `resetPasswordToken` only
        when the user had no password set prior to acceptance; otherwise the
        server returns an empty body (decode as an empty object).
      properties:
        resetPasswordToken:
          type: string
          description: 32-character hex nonce. Use to set the user's initial password.

    EmptyResponse:
      type: object
      description: Empty JSON object `{}`. Returned by operations that have no resource to return on success.

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

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

    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
