# Projects

Manage and query projects in your organization.

Projects are the top-level containers for test runs, specs, and test results. Each project has a unique project ID and can be configured with various settings.

## List projects

> Get all projects for your organization with optional pagination

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"LimitQuery":{"name":"limit","in":"query","required":false,"description":"Maximum number of items to return (default: 10, max: 50)","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},"StartingAfterQuery":{"name":"starting_after","in":"query","required":false,"description":"Cursor for pagination. Returns items after this cursor value.","schema":{"type":"string"}},"EndingBeforeQuery":{"name":"ending_before","in":"query","required":false,"description":"Cursor for pagination. Returns items before this cursor value.","schema":{"type":"string"}}},"schemas":{"ProjectsListResponse":{"type":"object","required":["status","has_more","data"],"properties":{"status":{"type":"string","enum":["OK"]},"has_more":{"type":"boolean","description":"Whether there are more items to fetch"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProjectWithCursor"}}}},"ProjectWithCursor":{"allOf":[{"$ref":"#/components/schemas/Project"},{"type":"object","properties":{"cursor":{"type":"string","description":"Cursor for pagination"}}}]},"Project":{"type":"object","required":["projectId","name","createdAt"],"properties":{"projectId":{"type":"string","description":"Unique identifier for the project"},"name":{"type":"string","description":"Human-readable name for the project"},"createdAt":{"type":"string","format":"date-time","description":"When the project was created"},"failFast":{"type":"boolean","nullable":true,"description":"Whether fail-fast is enabled for this project"},"inactivityTimeoutSeconds":{"type":"integer","nullable":true,"description":"Timeout in seconds after which inactive runs are marked as timed out"},"defaultBranchName":{"type":"string","nullable":true,"description":"Default branch name for the project"}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects":{"get":{"summary":"List projects","description":"Get all projects for your organization with optional pagination","operationId":"listProjects","tags":["Projects"],"parameters":[{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/StartingAfterQuery"},{"$ref":"#/components/parameters/EndingBeforeQuery"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectsListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}}}}
```

## Get project

> Get a single project by ID

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"ProjectIdPath":{"name":"projectId","in":"path","required":true,"description":"The project ID","schema":{"type":"string"}}},"schemas":{"ProjectResponse":{"type":"object","required":["status","data"],"properties":{"status":{"type":"string","enum":["OK"]},"data":{"$ref":"#/components/schemas/Project"}}},"Project":{"type":"object","required":["projectId","name","createdAt"],"properties":{"projectId":{"type":"string","description":"Unique identifier for the project"},"name":{"type":"string","description":"Human-readable name for the project"},"createdAt":{"type":"string","format":"date-time","description":"When the project was created"},"failFast":{"type":"boolean","nullable":true,"description":"Whether fail-fast is enabled for this project"},"inactivityTimeoutSeconds":{"type":"integer","nullable":true,"description":"Timeout in seconds after which inactive runs are marked as timed out"},"defaultBranchName":{"type":"string","nullable":true,"description":"Default branch name for the project"}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects/{projectId}":{"get":{"summary":"Get project","description":"Get a single project by ID","operationId":"getProject","tags":["Projects"],"parameters":[{"$ref":"#/components/parameters/ProjectIdPath"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## List project runs

> Get all runs for a project with optional pagination and filtering. Supports filtering by branch, tags, status, completion state, date range, search text, author, and pull request id (pr\_id maps to meta.pr.id).

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."},{"name":"Runs","description":"Query and manage test runs.\n\nA run represents a single execution of your test suite. Runs contain spec files\nwhich are executed across multiple instances. You can query run details, cancel\nruns, reset failed specs, and delete runs.\n\nPer-test outcome counters on specs (`results.stats`) use the `RecordedTestOutcome` vocabulary—see **components/schemas** for how **pending** and **skipped** differ from **failures**."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"ProjectIdPath":{"name":"projectId","in":"path","required":true,"description":"The project ID","schema":{"type":"string"}},"LimitQuery":{"name":"limit","in":"query","required":false,"description":"Maximum number of items to return (default: 10, max: 50)","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},"StartingAfterQuery":{"name":"starting_after","in":"query","required":false,"description":"Cursor for pagination. Returns items after this cursor value.","schema":{"type":"string"}},"EndingBeforeQuery":{"name":"ending_before","in":"query","required":false,"description":"Cursor for pagination. Returns items before this cursor value.","schema":{"type":"string"}},"TagsQuery":{"name":"tags[]","in":"query","required":false,"description":"Filter by tags (can be specified multiple times using bracket notation, e.g., tags[]=value1&tags[]=value2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"BranchesQuery":{"name":"branches[]","in":"query","required":false,"description":"Filter by git branch. Use trailing `*` for prefix match (e.g. `release/*`). Max 250 characters per value.","schema":{"type":"array","items":{"$ref":"#/components/schemas/BranchFilterQueryValue"}},"style":"form","explode":true},"AuthorsQuery":{"name":"authors[]","in":"query","required":false,"description":"Filter by git authors (can be specified multiple times using bracket notation, e.g., authors[]=author1&authors[]=author2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"RunFeedBranchQuery":{"name":"branch","in":"query","required":false,"deprecated":true,"description":"Deprecated. Use branches[] instead. Filter runs by git branch name (single branch). Supports a trailing `*` for prefix search (e.g., `release/*`). Branch value is limited to 250 characters (exact or wildcard).","schema":{"$ref":"#/components/schemas/BranchFilterQueryValue"}},"RunFeedTagQuery":{"name":"tag","in":"query","required":false,"deprecated":true,"description":"Deprecated. Use tags[] instead. Filter runs by tags (can be specified multiple times). Use tag_operator to control matching behavior.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"TagOperatorQuery":{"name":"tag_operator","in":"query","required":false,"description":"Logical operator for tag filtering. AND requires all tags to be present (default), OR requires any tag to be present.","schema":{"type":"string","enum":["AND","OR"],"default":"AND"}},"RunFeedSearchQuery":{"name":"search","in":"query","required":false,"description":"Search runs by ciBuildId or commit message. Case-insensitive.","schema":{"type":"string","maxLength":200}},"RunFeedPrIdQuery":{"name":"pr_id","in":"query","required":false,"description":"Filter runs by normalized pull request id (stored as meta.pr.id). Printable ASCII only, max 128 characters.","schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[!-~]+$"}},"RunFeedAuthorQuery":{"name":"author","in":"query","required":false,"deprecated":true,"description":"Deprecated. Use authors[] instead. Filter runs by git commit author name (can be specified multiple times).","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"RunStatusQuery":{"name":"status","in":"query","required":false,"description":"Filter runs by status (can be specified multiple times). PASSED: all tests passed, FAILED: some tests failed, RUNNING: run is in progress and passing, FAILING: run is in progress but has failures.","schema":{"type":"array","items":{"type":"string","enum":["PASSED","FAILED","RUNNING","FAILING"]}},"style":"form","explode":true},"RunCompletionStateQuery":{"name":"completion_state","in":"query","required":false,"description":"Filter runs by completion state (can be specified multiple times). COMPLETE: run finished normally, IN_PROGRESS: run is still executing, CANCELED: run was canceled, TIMEOUT: run timed out.","schema":{"type":"array","items":{"type":"string","enum":["COMPLETE","IN_PROGRESS","CANCELED","TIMEOUT"]}},"style":"form","explode":true},"OptionalDateStartQuery":{"name":"date_start","in":"query","required":false,"description":"Filter runs created on or after this date (ISO 8601 format).","schema":{"type":"string","format":"date-time"}},"OptionalDateEndQuery":{"name":"date_end","in":"query","required":false,"description":"Filter runs created before this date (ISO 8601 format).","schema":{"type":"string","format":"date-time"}}},"schemas":{"BranchFilterQueryValue":{"description":"Branch filter value: an exact branch name, or a trailing `*` for prefix match (e.g. `release/*`). Max 250 characters.","oneOf":[{"type":"string","description":"Exact branch name (must not contain `*` or spaces).","pattern":"^[!-)+-~]+$","maxLength":250},{"type":"string","description":"Prefix match: branch prefix followed by `*`.","pattern":"^[!-)+-~]+\\*$","maxLength":250}]},"RunsListResponse":{"type":"object","required":["status","has_more","data"],"properties":{"status":{"type":"string","enum":["OK"]},"has_more":{"type":"boolean","description":"Whether there are more items to fetch"},"data":{"type":"array","items":{"$ref":"#/components/schemas/RunFeedItem"}}}},"RunFeedItem":{"allOf":[{"type":"object","properties":{"runId":{"type":"string"},"projectId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"previousRunId":{"type":"string","nullable":true},"cursor":{"type":"string"},"timeout":{"type":"object","properties":{"isTimeout":{"type":"boolean"},"timeoutValueMs":{"type":"integer","nullable":true}}},"cancellation":{"type":"object","nullable":true},"groups":{"type":"array","items":{"$ref":"#/components/schemas/RunGroup"}},"meta":{"$ref":"#/components/schemas/RunMeta"},"completionState":{"type":"string"},"status":{"type":"string"}}}]},"RunGroup":{"type":"object","properties":{"groupId":{"type":"string"},"platform":{"type":"object","properties":{"osName":{"type":"string"},"osVersion":{"type":"string"},"browserName":{"type":"string"},"browserVersion":{"type":"string"}}},"tests":{"type":"object","description":"Rollup test counts for this group (`RecordedTestOutcome` semantics; see components/schemas).","properties":{"overall":{"type":"integer"},"passes":{"type":"integer"},"failures":{"type":"integer"},"pending":{"type":"integer","description":"**pending** outcomes (intentional skips)."},"skipped":{"type":"integer","description":"**skipped** outcomes (separate from `failures` here; see schema for Tests Explorer difference)."},"flaky":{"type":"integer"}}}}},"RunMeta":{"type":"object","properties":{"ciBuildId":{"type":"string","description":"CI build identifier"},"pr":{"type":"object","nullable":true,"description":"Normalized pull request metadata when the run is associated with a PR","properties":{"id":{"type":"string","nullable":true},"link":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"destination":{"type":"string","nullable":true}}},"commit":{"type":"object","properties":{"sha":{"type":"string"},"branch":{"type":"string"},"authorName":{"type":"string"},"authorEmail":{"type":"string"},"message":{"type":"string"},"remoteOrigin":{"type":"string"}}},"framework":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}}}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects/{projectId}/runs":{"get":{"summary":"List project runs","description":"Get all runs for a project with optional pagination and filtering. Supports filtering by branch, tags, status, completion state, date range, search text, author, and pull request id (pr_id maps to meta.pr.id).","operationId":"listProjectRuns","tags":["Projects","Runs"],"parameters":[{"$ref":"#/components/parameters/ProjectIdPath"},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/StartingAfterQuery"},{"$ref":"#/components/parameters/EndingBeforeQuery"},{"$ref":"#/components/parameters/TagsQuery"},{"$ref":"#/components/parameters/BranchesQuery"},{"$ref":"#/components/parameters/AuthorsQuery"},{"$ref":"#/components/parameters/RunFeedBranchQuery"},{"$ref":"#/components/parameters/RunFeedTagQuery"},{"$ref":"#/components/parameters/TagOperatorQuery"},{"$ref":"#/components/parameters/RunFeedSearchQuery"},{"$ref":"#/components/parameters/RunFeedPrIdQuery"},{"$ref":"#/components/parameters/RunFeedAuthorQuery"},{"$ref":"#/components/parameters/RunStatusQuery"},{"$ref":"#/components/parameters/RunCompletionStateQuery"},{"$ref":"#/components/parameters/OptionalDateStartQuery"},{"$ref":"#/components/parameters/OptionalDateEndQuery"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunsListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## List pull requests (grouped runs)

> Returns pull-request cards for the project: runs with a canonical \`meta.pr.id\` are grouped per PR. Each card includes aggregate stats, the latest run (by ObjectId), and a preview of recent runs (count controlled by \`runs\_per\_pr\`, default 1, max 10). Filter by branch (PR source with commit-branch fallback), tags, authors, and by status using the \*\*latest\*\* run’s derived status (same rules as the run feed). Supports prev/next cursor-based pagination via \`starting\_after\` (forward) and \`ending\_before\` (backward). The sort key (\`latestRun\`) is mutable, so cursor positions are eventually-consistent: a PR may shift between pages if it receives a new run. Clients must apply identical filters across page requests. Each card exposes an opaque \`cursor\` for the next page; do not send both cursors in one request.

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."},{"name":"Runs","description":"Query and manage test runs.\n\nA run represents a single execution of your test suite. Runs contain spec files\nwhich are executed across multiple instances. You can query run details, cancel\nruns, reset failed specs, and delete runs.\n\nPer-test outcome counters on specs (`results.stats`) use the `RecordedTestOutcome` vocabulary—see **components/schemas** for how **pending** and **skipped** differ from **failures**."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"ProjectIdPath":{"name":"projectId","in":"path","required":true,"description":"The project ID","schema":{"type":"string"}},"LimitQuery":{"name":"limit","in":"query","required":false,"description":"Maximum number of items to return (default: 10, max: 50)","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},"StartingAfterQuery":{"name":"starting_after","in":"query","required":false,"description":"Cursor for pagination. Returns items after this cursor value.","schema":{"type":"string"}},"EndingBeforeQuery":{"name":"ending_before","in":"query","required":false,"description":"Cursor for pagination. Returns items before this cursor value.","schema":{"type":"string"}},"PullRequestRunsPerPrQuery":{"name":"runs_per_pr","in":"query","required":false,"description":"How many most recent runs to include in each card’s `timelinePreview` (does not cap total stats).","schema":{"type":"integer","minimum":1,"maximum":10,"default":1}},"TagsQuery":{"name":"tags[]","in":"query","required":false,"description":"Filter by tags (can be specified multiple times using bracket notation, e.g., tags[]=value1&tags[]=value2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"BranchesQuery":{"name":"branches[]","in":"query","required":false,"description":"Filter by git branch. Use trailing `*` for prefix match (e.g. `release/*`). Max 250 characters per value.","schema":{"type":"array","items":{"$ref":"#/components/schemas/BranchFilterQueryValue"}},"style":"form","explode":true},"AuthorsGlobQuery":{"name":"authors[]","in":"query","required":false,"description":"Filter by git author name. Use trailing `*` for prefix match (e.g. `Adam*`). May contain spaces. Max 250 characters per value.","schema":{"type":"array","items":{"$ref":"#/components/schemas/AuthorGlobFilterQueryValue"}},"style":"form","explode":true},"TagOperatorQuery":{"name":"tag_operator","in":"query","required":false,"description":"Logical operator for tag filtering. AND requires all tags to be present (default), OR requires any tag to be present.","schema":{"type":"string","enum":["AND","OR"],"default":"AND"}},"RunStatusQuery":{"name":"status","in":"query","required":false,"description":"Filter runs by status (can be specified multiple times). PASSED: all tests passed, FAILED: some tests failed, RUNNING: run is in progress and passing, FAILING: run is in progress but has failures.","schema":{"type":"array","items":{"type":"string","enum":["PASSED","FAILED","RUNNING","FAILING"]}},"style":"form","explode":true}},"schemas":{"BranchFilterQueryValue":{"description":"Branch filter value: an exact branch name, or a trailing `*` for prefix match (e.g. `release/*`). Max 250 characters.","oneOf":[{"type":"string","description":"Exact branch name (must not contain `*` or spaces).","pattern":"^[!-)+-~]+$","maxLength":250},{"type":"string","description":"Prefix match: branch prefix followed by `*`.","pattern":"^[!-)+-~]+\\*$","maxLength":250}]},"AuthorGlobFilterQueryValue":{"description":"Author name filter value: an exact name, or a trailing `*` for prefix match (e.g. `Adam*`). May contain spaces. Max 250 characters.","oneOf":[{"type":"string","description":"Exact author name (must not contain `*`).","pattern":"^[ -)+-~]+$","maxLength":250},{"type":"string","description":"Prefix match: name prefix followed by `*`.","pattern":"^[ -)+-~]+\\*$","maxLength":250}]},"PullRequestsListResponse":{"type":"object","required":["status","has_more","data"],"properties":{"status":{"type":"string","enum":["OK"]},"has_more":{"type":"boolean","description":"Whether more PR rows exist after this page"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PullRequestCard"}}}},"PullRequestCard":{"type":"object","required":["pr","stats","latestRun","timelinePreview","hasMoreRuns","cursor"],"properties":{"pr":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Canonical pull request id (`meta.pr.id`)"},"prNumber":{"type":"string","nullable":true,"description":"Numeric PR/MR number extracted from the canonical id (e.g. \"42\" from \"github:github.com/org/repo#42\")"},"link":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"destination":{"type":"string","nullable":true}}},"stats":{"$ref":"#/components/schemas/PullRequestStats"},"latestRun":{"$ref":"#/components/schemas/RunFeedItem"},"timelinePreview":{"type":"array","description":"Most recent runs for this PR (newest first), up to `runs_per_pr` from the request (default 1, max 10)","items":{"$ref":"#/components/schemas/RunFeedItem"}},"hasMoreRuns":{"type":"boolean","description":"True when more runs exist for this PR than returned in `timelinePreview`"},"cursor":{"type":"string","description":"Opaque pagination cursor for this PR row; pass as `starting_after` for the next page"}}},"PullRequestStats":{"type":"object","required":["total","passed","failed","running","failing"],"properties":{"total":{"type":"integer","description":"Number of runs grouped under this pull request"},"passed":{"type":"integer","description":"Runs with PASSED status"},"failed":{"type":"integer","description":"Runs with FAILED status"},"running":{"type":"integer","description":"Runs with RUNNING status"},"failing":{"type":"integer","description":"Runs with FAILING status"}}},"RunFeedItem":{"allOf":[{"type":"object","properties":{"runId":{"type":"string"},"projectId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","nullable":true},"tags":{"type":"array","items":{"type":"string"}},"previousRunId":{"type":"string","nullable":true},"cursor":{"type":"string"},"timeout":{"type":"object","properties":{"isTimeout":{"type":"boolean"},"timeoutValueMs":{"type":"integer","nullable":true}}},"cancellation":{"type":"object","nullable":true},"groups":{"type":"array","items":{"$ref":"#/components/schemas/RunGroup"}},"meta":{"$ref":"#/components/schemas/RunMeta"},"completionState":{"type":"string"},"status":{"type":"string"}}}]},"RunGroup":{"type":"object","properties":{"groupId":{"type":"string"},"platform":{"type":"object","properties":{"osName":{"type":"string"},"osVersion":{"type":"string"},"browserName":{"type":"string"},"browserVersion":{"type":"string"}}},"tests":{"type":"object","description":"Rollup test counts for this group (`RecordedTestOutcome` semantics; see components/schemas).","properties":{"overall":{"type":"integer"},"passes":{"type":"integer"},"failures":{"type":"integer"},"pending":{"type":"integer","description":"**pending** outcomes (intentional skips)."},"skipped":{"type":"integer","description":"**skipped** outcomes (separate from `failures` here; see schema for Tests Explorer difference)."},"flaky":{"type":"integer"}}}}},"RunMeta":{"type":"object","properties":{"ciBuildId":{"type":"string","description":"CI build identifier"},"pr":{"type":"object","nullable":true,"description":"Normalized pull request metadata when the run is associated with a PR","properties":{"id":{"type":"string","nullable":true},"link":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"destination":{"type":"string","nullable":true}}},"commit":{"type":"object","properties":{"sha":{"type":"string"},"branch":{"type":"string"},"authorName":{"type":"string"},"authorEmail":{"type":"string"},"message":{"type":"string"},"remoteOrigin":{"type":"string"}}},"framework":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}}}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects/{projectId}/pull-requests":{"get":{"summary":"List pull requests (grouped runs)","description":"Returns pull-request cards for the project: runs with a canonical `meta.pr.id` are grouped per PR. Each card includes aggregate stats, the latest run (by ObjectId), and a preview of recent runs (count controlled by `runs_per_pr`, default 1, max 10). Filter by branch (PR source with commit-branch fallback), tags, authors, and by status using the **latest** run’s derived status (same rules as the run feed). Supports prev/next cursor-based pagination via `starting_after` (forward) and `ending_before` (backward). The sort key (`latestRun`) is mutable, so cursor positions are eventually-consistent: a PR may shift between pages if it receives a new run. Clients must apply identical filters across page requests. Each card exposes an opaque `cursor` for the next page; do not send both cursors in one request.","operationId":"listProjectPullRequests","tags":["Projects","Runs"],"parameters":[{"$ref":"#/components/parameters/ProjectIdPath"},{"$ref":"#/components/parameters/LimitQuery"},{"$ref":"#/components/parameters/StartingAfterQuery"},{"$ref":"#/components/parameters/EndingBeforeQuery"},{"$ref":"#/components/parameters/PullRequestRunsPerPrQuery"},{"$ref":"#/components/parameters/TagsQuery"},{"$ref":"#/components/parameters/BranchesQuery"},{"$ref":"#/components/parameters/AuthorsGlobQuery"},{"$ref":"#/components/parameters/TagOperatorQuery"},{"$ref":"#/components/parameters/RunStatusQuery"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PullRequestsListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## Get project insights

> Get aggregated run and test metrics for a project within a date range

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"ProjectIdPath":{"name":"projectId","in":"path","required":true,"description":"The project ID","schema":{"type":"string"}},"DateStartQuery":{"name":"date_start","in":"query","required":true,"description":"Start date in ISO 8601 format","schema":{"type":"string","format":"date-time"}},"DateEndQuery":{"name":"date_end","in":"query","required":true,"description":"End date in ISO 8601 format","schema":{"type":"string","format":"date-time"}},"ResolutionQuery":{"name":"resolution","in":"query","required":false,"description":"Time resolution for histogram data","schema":{"type":"string","enum":["1h","1d","1w"],"default":"1d"}},"TagsQuery":{"name":"tags[]","in":"query","required":false,"description":"Filter by tags (can be specified multiple times using bracket notation, e.g., tags[]=value1&tags[]=value2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"BranchesQuery":{"name":"branches[]","in":"query","required":false,"description":"Filter by git branch. Use trailing `*` for prefix match (e.g. `release/*`). Max 250 characters per value.","schema":{"type":"array","items":{"$ref":"#/components/schemas/BranchFilterQueryValue"}},"style":"form","explode":true},"GroupsQuery":{"name":"groups[]","in":"query","required":false,"description":"Filter by groups (can be specified multiple times using bracket notation, e.g., groups[]=group1&groups[]=group2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},"AuthorsQuery":{"name":"authors[]","in":"query","required":false,"description":"Filter by git authors (can be specified multiple times using bracket notation, e.g., authors[]=author1&authors[]=author2)","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true}},"schemas":{"BranchFilterQueryValue":{"description":"Branch filter value: an exact branch name, or a trailing `*` for prefix match (e.g. `release/*`). Max 250 characters.","oneOf":[{"type":"string","description":"Exact branch name (must not contain `*` or spaces).","pattern":"^[!-)+-~]+$","maxLength":250},{"type":"string","description":"Prefix match: branch prefix followed by `*`.","pattern":"^[!-)+-~]+\\*$","maxLength":250}]},"ProjectInsightsResponse":{"type":"object","required":["status","data"],"properties":{"status":{"type":"string","enum":["OK"]},"data":{"type":"object","properties":{"projectId":{"type":"string"},"orgId":{"type":"string"},"dateStart":{"type":"string","format":"date-time"},"dateEnd":{"type":"string","format":"date-time"},"resolution":{"type":"string"},"results":{"type":"object","properties":{"overall":{"type":"object","properties":{"runs":{"$ref":"#/components/schemas/RunMetrics"},"tests":{"$ref":"#/components/schemas/TestMetrics"}}},"timeline":{"type":"array","items":{"type":"object","additionalProperties":{"type":"object","properties":{"runs":{"$ref":"#/components/schemas/RunMetrics"},"tests":{"$ref":"#/components/schemas/TestMetrics"}}}}}}}}}}},"RunMetrics":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of runs"},"cancelled":{"type":"integer","description":"Cancelled runs"},"timeouts":{"type":"integer","description":"Timed out runs"},"completed":{"type":"integer","description":"Fully completed runs"},"failed":{"type":"integer","description":"Failed runs"},"passed":{"type":"integer","description":"Passed runs"},"nonFullyReported":{"type":"integer","description":"Runs that did not fully report"},"avgDurationSeconds":{"type":"number","description":"Average run duration in seconds"},"avgSuccessRate":{"type":"number","description":"Average success rate (0-1)"}}},"TestMetrics":{"type":"object","properties":{"total":{"type":"integer","description":"Total test executions"},"failed":{"type":"integer","description":"Failed tests"},"passed":{"type":"integer","description":"Passed tests"},"pending":{"type":"integer","description":"**pending** outcomes (intentional skips). See `RecordedTestOutcome`."},"skipped":{"type":"integer","description":"**skipped** outcomes (runner skip / interrupted). See `RecordedTestOutcome`."},"flaky":{"type":"integer","description":"Flaky tests"}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects/{projectId}/insights":{"get":{"summary":"Get project insights","description":"Get aggregated run and test metrics for a project within a date range","operationId":"getProjectInsights","tags":["Projects"],"parameters":[{"$ref":"#/components/parameters/ProjectIdPath"},{"$ref":"#/components/parameters/DateStartQuery"},{"$ref":"#/components/parameters/DateEndQuery"},{"$ref":"#/components/parameters/ResolutionQuery"},{"$ref":"#/components/parameters/TagsQuery"},{"$ref":"#/components/parameters/BranchesQuery"},{"$ref":"#/components/parameters/GroupsQuery"},{"$ref":"#/components/parameters/AuthorsQuery"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInsightsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```

## List project terms for a single type

> Returns cursor-paginated terms for one kind (\`tag\`, \`branch\`, \`authorName\`, etc.). Rows are ordered by each term's last update time; use \`dir\` as \`asc\` or \`desc\` (default \`desc\`, most recently updated first). Page with \`starting\_after\` or \`ending\_before\` using the opaque \`cursor\` value from a returned item; do not send both cursors in one request. Reuse the same \`dir\`, \`limit\`, search filter, and path when requesting additional pages.

```json
{"openapi":"3.0.2","info":{"title":"Currents REST API","version":"1.0.0"},"tags":[{"name":"Projects","description":"Manage and query projects in your organization.\n\nProjects are the top-level containers for test runs, specs, and test results.\nEach project has a unique project ID and can be configured with various settings."}],"servers":[{"url":"https://api.currents.dev/v1","description":"Production API"},{"url":"https://api-staging.currents.dev/v1","description":"Staging API"},{"url":"http://localhost:4000/v1","description":"Local development"}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication using Bearer token"}},"parameters":{"ProjectIdPath":{"name":"projectId","in":"path","required":true,"description":"The project ID","schema":{"type":"string"}},"TermTypePath":{"name":"termType","in":"path","required":true,"description":"Term kind to list","schema":{"type":"string","enum":["tag","group","branch","authorName","authorEmail","framework","frameworkVersion","clientVersion","ann_type","ann_desc"]}},"TermsLimitQuery":{"name":"limit","in":"query","required":false,"description":"Maximum number of terms to return (default: 100, max: 100)","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}},"SortDirectionQuery":{"name":"dir","in":"query","required":false,"description":"Sort direction: `desc` (default) or `asc`.","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}},"StartingAfterQuery":{"name":"starting_after","in":"query","required":false,"description":"Cursor for pagination. Returns items after this cursor value.","schema":{"type":"string"}},"EndingBeforeQuery":{"name":"ending_before","in":"query","required":false,"description":"Cursor for pagination. Returns items before this cursor value.","schema":{"type":"string"}},"TermsSearchQuery":{"name":"search","in":"query","required":false,"description":"Optional case-insensitive prefix filter on term values (up to 128 characters)","schema":{"type":"string","maxLength":128}}},"schemas":{"ProjectTermsListResponse":{"type":"object","required":["status","has_more","data"],"properties":{"status":{"type":"string","enum":["OK"]},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProjectTermsTermItem"},"description":"Ordered term rows for the requested type"}}},"ProjectTermsTermItem":{"type":"object","required":["id","name","count","updated_at","cursor"],"properties":{"id":{"type":"string","description":"Unique identifier for this term"},"name":{"type":"string","description":"Term value"},"count":{"type":"integer","description":"Number of times this term was used"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"Last time the term stats were updated"},"cursor":{"type":"string","description":"Opaque pagination token for this row. Pass as `starting_after` or `ending_before` on follow-up requests to fetch the next or previous page along the same sort order."}}},"ErrorResponse":{"type":"object","required":["status","error"],"properties":{"status":{"type":"string","enum":["FAILED"]},"error":{"type":"string","description":"Error message"}}}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/projects/{projectId}/terms/{termType}":{"get":{"summary":"List project terms for a single type","description":"Returns cursor-paginated terms for one kind (`tag`, `branch`, `authorName`, etc.). Rows are ordered by each term's last update time; use `dir` as `asc` or `desc` (default `desc`, most recently updated first). Page with `starting_after` or `ending_before` using the opaque `cursor` value from a returned item; do not send both cursors in one request. Reuse the same `dir`, `limit`, search filter, and path when requesting additional pages.","operationId":"listProjectTermsByType","tags":["Projects"],"parameters":[{"$ref":"#/components/parameters/ProjectIdPath"},{"$ref":"#/components/parameters/TermTypePath"},{"$ref":"#/components/parameters/TermsLimitQuery"},{"$ref":"#/components/parameters/SortDirectionQuery"},{"$ref":"#/components/parameters/StartingAfterQuery"},{"$ref":"#/components/parameters/EndingBeforeQuery"},{"$ref":"#/components/parameters/TermsSearchQuery"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectTermsListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.currents.dev/api/resources/projects.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
