{
  "openapi": "3.1.0",
  "info": {
    "title": "cubic Analytics API",
    "version": "1.0.0",
    "description": "Export pull request analytics with a personal or organization API key. Access requires Pro or Max."
  },
  "externalDocs": {
    "url": "https://docs.cubic.dev/analytics/api"
  },
  "servers": [
    {
      "url": "https://www.cubic.dev"
    }
  ],
  "security": [
    {
      "analyticsApiKey": []
    }
  ],
  "paths": {
    "/api/analytics/v1/prs": {
      "get": {
        "operationId": "listPullRequestAnalytics",
        "summary": "List pull request analytics",
        "description": "Export metrics for merged pull requests reviewed by cubic.",
        "parameters": [
          {
            "name": "org",
            "in": "query",
            "required": true,
            "description": "GitHub organization or owner whose pull request analytics you want to fetch. Surrounding whitespace is trimmed before validation.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          },
          {
            "name": "repo",
            "in": "query",
            "required": false,
            "description": "Filter to one repository under the selected owner. Surrounding whitespace is trimmed before validation; blank values are ignored.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "description": "Start of the review date window. Accepts ISO 8601 timestamps or UTC date-only values such as 2026-04-01. Surrounding whitespace is trimmed before validation; blank values are ignored. If neither date bound is set, cubic uses the last 30 days, 7 days, or 24 hours based on the installation's available history.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "description": "End of the review date window. A UTC date-only value includes the full day through 23:59:59.999Z. Surrounding whitespace is trimmed before validation; blank values are ignored.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "perPage",
            "in": "query",
            "required": false,
            "description": "Number of results per page, from 1 to 100. Values outside this range return 400.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Copy nextCursor unchanged from the preceding response. Keep the same organization, filters, and date window across pages. Surrounding whitespace is trimmed before validation; blank values are ignored.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of pull request analytics. nextCursor is null on the last page. Recent updates may take a short time to appear due to read-replica lag.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsPrPage"
                },
                "example": {
                  "data": [
                    {
                      "org": "acme",
                      "repo": "backend",
                      "prNumber": 4645,
                      "author": "john-doe",
                      "createdAt": "2026-04-16T12:00:00.000Z",
                      "mergedAt": "2026-04-17T12:00:00.000Z",
                      "linesAdded": 543,
                      "linesDeleted": 12,
                      "numberOfCubicIssuesFlagged": 5,
                      "numberOfCubicIssuesFixed": 4,
                      "cubicFirstReviewedAt": "2026-04-17T00:00:00.000Z",
                      "totalAiLinesAuthored": 523
                    }
                  ],
                  "nextCursor": "2026-04-16T12:00:00.000Z|102"
                }
              }
            }
          },
          "400": {
            "description": "Missing org, invalid query parameters or cursor, or an invalid date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "401": {
            "description": "Missing bearer token, invalid or expired API key, or blocked key owner or organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "403": {
            "description": "The key cannot access the organization, the installation is blocked, or the organization is not on Pro or Max. An organization key reads only its own organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "404": {
            "description": "The organization cannot be resolved to a cubic installation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "429": {
            "description": "The rate limit is exhausted. Retry after the Retry-After header. Personal and organization keys count toward the same limits as the Members API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "500": {
            "description": "API key verification, access checks, or analytics processing failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          },
          "503": {
            "description": "cubic could not verify the key or check the rate limit. Retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "analyticsApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A personal API key (cbk_) or an organization API key (cok_) with any access, from Settings > API, CLI & MCP. Requires Pro or Max. Analytics API keys that start with cak_ keep working, but cubic no longer creates them."
      }
    },
    "schemas": {
      "AnalyticsPrPage": {
        "type": "object",
        "required": ["data", "nextCursor"],
        "properties": {
          "data": {
            "type": "array",
            "description": "Merged pull requests with at least one completed cubic review in the selected date window, ordered by merge time, newest first.",
            "items": {
              "$ref": "#/components/schemas/AnalyticsPrRow"
            }
          },
          "nextCursor": {
            "type": ["string", "null"],
            "description": "Cursor for the next page, or null on the last page."
          }
        }
      },
      "AnalyticsPrRow": {
        "type": "object",
        "required": [
          "org",
          "repo",
          "prNumber",
          "author",
          "createdAt",
          "mergedAt",
          "linesAdded",
          "linesDeleted",
          "numberOfCubicIssuesFlagged",
          "numberOfCubicIssuesFixed",
          "cubicFirstReviewedAt",
          "totalAiLinesAuthored"
        ],
        "properties": {
          "org": {
            "type": "string",
            "description": "GitHub owner used for the request."
          },
          "repo": {
            "type": "string",
            "description": "Repository name."
          },
          "prNumber": {
            "type": "integer",
            "description": "Pull request number."
          },
          "author": {
            "type": "string",
            "description": "GitHub login of the pull request author."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Pull request creation timestamp in ISO 8601 format."
          },
          "mergedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Pull request merge timestamp in ISO 8601 format."
          },
          "linesAdded": {
            "type": ["integer", "null"],
            "description": "Lines added in the pull request, or `null` when unavailable."
          },
          "linesDeleted": {
            "type": ["integer", "null"],
            "description": "Lines deleted in the pull request, or `null` when unavailable."
          },
          "numberOfCubicIssuesFlagged": {
            "type": "integer",
            "description": "Posted cubic findings on the merged pull request, created within the requested date range."
          },
          "numberOfCubicIssuesFixed": {
            "type": "integer",
            "description": "Posted findings in the requested date range with a detected code change (`aiAddressed = true`). Positive feedback alone does not count as a fix, and downvotes do not erase detected fixes."
          },
          "cubicFirstReviewedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of the first completed cubic review for the pull request."
          },
          "totalAiLinesAuthored": {
            "type": ["integer", "null"],
            "description": "AI-authored lines attributed to the pull request, capped at its total added and deleted lines; null when no AI authorship data is available."
          }
        }
      },
      "AnalyticsError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "An explanation of the failure."
          },
          "details": {
            "description": "Optional validation or error details. The shape depends on the failure."
          }
        }
      }
    }
  }
}
