> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trunk.io/llms.txt
> Use this file to discover all available pages before exploring further.

# List and search test cases

> Lists test cases, by default across every test collection in your organization — pass `testCollectionId` to narrow to one. Each row carries the `id` to pass to the per-test endpoints.

`q` searches name, file, class and parent as case-insensitive substrings. Several words are separate requirements, not a phrase — `q=flaky slow` returns tests matching both, in any position. It also accepts per-field qualifiers mixed freely with those words: `q=file:checkout flaky class:PaymentTest`. Qualifier keys are case-sensitive, and an unrecognized prefix is treated as literal text rather than guessed at.

Rows omit `quarantineOverride`, `activeMonitors`, `firstSeenAt` and `lastFailureAt` — fetch a single test case for those.



## OpenAPI

````yaml /openapi-v2.json get /v2/test-cases
openapi: 3.1.0
info:
  title: Trunk API
  version: 2.0.0
  description: >-
    The Trunk public API. Every endpoint is scoped to a single organization and
    requires a credential.


    ## Authentication


    Send **either** an org API key as `Authorization: Bearer <key>`, **or** a
    short-lived first-party token in the `x-trunk-token` header. Existing v1 API
    tokens work unchanged as org API keys.


    The two are not interchangeable for writes. A first-party token obtained by
    **exchanging an org API key** reads merge queues but cannot mutate them — a
    merge-queue mutation forwards the key itself to the merge service, and an
    exchanged token carries no copy of it, so those requests answer `403
    INSUFFICIENT_PERMISSIONS`. A machine caller that mutates merge queues should
    send its org API key directly on `Authorization` for every request. Tokens
    obtained through the CLI device flow are unaffected.


    **Merge-queue authorization is broad today.** The API does not offer an
    enqueue-only capability: organization members and org API keys authorized
    for merge-queue writes can also change configuration (including
    `requiredStatuses`), pause, drain and delete queues. Use such credentials as
    privileged operators, not narrowly scoped CI submission tokens.


    ## Errors


    Every 4xx and 5xx response is a `ErrorResponse`: `{ "error": { "code",
    "message" } }`. Switch on `code`, which is stable; `message` is prose and
    may change at any time.


    **An authorization denial on a read is reported as `404`, not `403`** —
    whether a resource exists must not leak across organizations, so a resource
    in another organization is indistinguishable from one that does not exist.
    `403 INSUFFICIENT_PERMISSIONS` means the credential is valid but is not
    permitted the action (or carries no organization).


    Every response carries an `X-Request-Id` header. Quote it in support
    requests.


    ## Rate limiting


    Requests are limited per organization (currently 100 requests/second).
    Exceeding it returns `429` with `RATE_LIMIT_EXCEEDED`; retry with backoff.
    Note that the limit is enforced at the edge, which does not yet wrap its
    `429` in the `ErrorResponse` envelope described above — treat any `429` as
    rate limiting regardless of body.


    ## Compatibility


    Within v2, Trunk may add: new endpoints, new **optional** response fields,
    new members to any enum (including new `code` values, new queue-entry
    states, and new providers), and new optional request parameters. **Clients
    must tolerate values and fields they do not recognize** rather than failing
    closed — a client that switches exhaustively over an enum will break when a
    member is added. Removals and semantic changes require a new major version.


    ## Provider support


    Merge queues, verification runs and checks are **GitHub-only** today;
    `Check.provider` has a single member for that reason. Impacted-target
    uploads additionally require a `github.com` repository named `owner/repo`.
    The `Provider` enum lists values the repository index can store, not
    surfaces that are fully supported.


    ## Not yet available


    A pull request's **position** in its queue is not exposed, and list
    responses are not ordered by queue position. There are no webhooks —
    monitoring is by polling. `Idempotency-Key` is not yet honored, so a
    mutation that times out must be reconciled by reading the resource back
    rather than blindly retried.
  contact:
    name: Trunk Support
    url: https://docs.trunk.io
servers:
  - url: https://api.trunk.io
    description: Production
security:
  - orgKey: []
  - trunkToken: []
tags:
  - name: mergeQueues
    x-group: Merge Queues
  - name: pullRequests
    x-group: Pull Requests
  - name: mergeQueueVerificationRuns
    x-group: Verification Runs
  - name: impactedTargets
    x-group: Impacted Targets
  - name: repositories
    x-group: Repositories
  - name: dynamicCi
    x-group: Dynamic CI
  - name: testCollections
    x-group: Test Collections
  - name: testCases
    x-group: Test Cases
paths:
  /v2/test-cases:
    get:
      tags:
        - testCases
      summary: List and search test cases
      description: >-
        Lists test cases, by default across every test collection in your
        organization — pass `testCollectionId` to narrow to one. Each row
        carries the `id` to pass to the per-test endpoints.


        `q` searches name, file, class and parent as case-insensitive
        substrings. Several words are separate requirements, not a phrase —
        `q=flaky slow` returns tests matching both, in any position. It also
        accepts per-field qualifiers mixed freely with those words:
        `q=file:checkout flaky class:PaymentTest`. Qualifier keys are
        case-sensitive, and an unrecognized prefix is treated as literal text
        rather than guessed at.


        Rows omit `quarantineOverride`, `activeMonitors`, `firstSeenAt` and
        `lastFailureAt` — fetch a single test case for those.
      operationId: testCases.list
      parameters:
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
            description: Page size, between 1 and 100.
            example: 25
          required: false
          description: Page size, between 1 and 100.
          name: limit
          in: query
        - schema:
            type: string
            description: >-
              Opaque cursor from a previous response's `nextCursor`. A cursor is
              bound to the operation and filter it was minted for.
            example: AQEKZm9vYmFy
          required: false
          description: >-
            Opaque cursor from a previous response's `nextCursor`. A cursor is
            bound to the operation and filter it was minted for.
          name: cursor
          in: query
        - schema:
            type: boolean
            default: false
            description: >-
              Set true to include `total` in the response. Costs an extra COUNT
              query, so it is off by default.
            example: false
          required: false
          description: >-
            Set true to include `total` in the response. Costs an extra COUNT
            query, so it is off by default.
          name: includeTotal
          in: query
        - schema:
            type: string
            minLength: 1
            description: >-
              Narrow to one collection, by its id. Omitted searches every
              collection in your organization.
            example: aB3xY9kQ
          required: false
          description: >-
            Narrow to one collection, by its id. Omitted searches every
            collection in your organization.
          name: testCollectionId
          in: query
        - schema:
            type: string
            minLength: 1
            description: 'Comma-separated statuses to include: `healthy`, `flaky`, `broken`.'
            example: flaky,broken
          required: false
          description: 'Comma-separated statuses to include: `healthy`, `flaky`, `broken`.'
          name: status
          in: query
        - schema:
            type: string
            minLength: 1
            description: >-
              Free-text search, with optional `file:` / `class:` / `parent:` /
              `name:` qualifiers.
            example: file:checkout flaky
          required: false
          description: >-
            Free-text search, with optional `file:` / `class:` / `parent:` /
            `name:` qualifiers.
          name: q
          in: query
        - schema:
            type: string
            minLength: 1
            description: Substring match on the test's file path.
            example: src/payments
          required: false
          description: Substring match on the test's file path.
          name: file
          in: query
        - schema:
            type: string
            minLength: 1
            description: Exact match on one of the test's code owners.
            example: '@acme/platform'
          required: false
          description: Exact match on one of the test's code owners.
          name: owner
          in: query
        - schema:
            type: string
            minLength: 1
            description: Exact match on the test's variant.
            example: ubuntu-22.04
          required: false
          description: Exact match on the test's variant.
          name: variant
          in: query
        - schema:
            type: string
            enum:
              - metrics
            description: >-
              Set to `metrics` to embed each test's metrics for `period` in the
              same response, instead of fetching them per test.
            example: metrics
          required: false
          description: >-
            Set to `metrics` to embed each test's metrics for `period` in the
            same response, instead of fetching them per test.
          name: include
          in: query
        - schema:
            type: string
            minLength: 1
            description: >-
              Only tests carrying this label, by name. Matched exactly,
              including case, since two labels may differ only in case. An
              unknown name returns an empty page, not an error.
            example: slow-on-prs
          required: false
          description: >-
            Only tests carrying this label, by name. Matched exactly, including
            case, since two labels may differ only in case. An unknown name
            returns an empty page, not an error.
          name: label
          in: query
        - schema:
            type: string
            enum:
              - quarantined
              - notQuarantined
            description: >-
              Filter on effective quarantine state, derived from the test's
              status, its collection's quarantine settings and any per-test
              override.
            example: quarantined
          required: false
          description: >-
            Filter on effective quarantine state, derived from the test's
            status, its collection's quarantine settings and any per-test
            override.
          name: quarantineState
          in: query
        - schema:
            type: boolean
            description: >-
              Filter on whether the manual 'flag as flaky' mark is currently
              set.
            example: true
          required: false
          description: Filter on whether the manual 'flag as flaky' mark is currently set.
          name: markedFlaky
          in: query
        - schema:
            type: boolean
            description: >-
              Only tests that first became flaky inside `period`. Requires
              `period`.
            example: true
          required: false
          description: >-
            Only tests that first became flaky inside `period`. Requires
            `period`.
          name: newlyFlaky
          in: query
        - schema:
            type: string
            enum:
              - 1d
              - 7d
              - 14d
              - 30d
            description: >-
              Metrics window for `include=metrics`, any metric `sort`, and
              `newlyFlaky`. Required by each of them.
            example: 7d
          required: false
          description: >-
            Metrics window for `include=metrics`, any metric `sort`, and
            `newlyFlaky`. Required by each of them.
          name: period
          in: query
        - schema:
            type: string
            enum:
              - name
              - failureRate
              - durationP95
              - prsImpacted
              - lastRunAt
              - lastFailureAt
            default: name
            description: >-
              Field to sort by. A metric sort requires `period`, and excludes
              tests with no comparable value in it — no runs at all for most
              metrics, and additionally no failure for `lastFailureAt`.
          required: false
          description: >-
            Field to sort by. A metric sort requires `period`, and excludes
            tests with no comparable value in it — no runs at all for most
            metrics, and additionally no failure for `lastFailureAt`.
          name: sort
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: >-
              Sort direction. Defaults per sort: ascending for `name`,
              descending for every metric sort, which order worst-first.
          required: false
          description: >-
            Sort direction. Defaults per sort: ascending for `name`, descending
            for every metric sort, which order worst-first.
          name: direction
          in: query
      responses:
        '200':
          description: A page of test cases
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestCaseList'
        '400':
          description: '`VALIDATION_FAILED` | `INVALID_CURSOR`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: '`AUTHENTICATION_REQUIRED` | `INVALID_TOKEN`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: '`RATE_LIMIT_EXCEEDED`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: '`INTERNAL_ERROR`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    TestCaseList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TestCaseSummary'
          maxItems: 100
        nextCursor:
          type:
            - string
            - 'null'
          description: >-
            Opaque cursor for the next page, or null on the last page. Pass it
            back verbatim as `cursor`; do not construct or inspect one.
          example: AQEKZm9vYmFy
        hasMore:
          type: boolean
          description: True when another page is available.
          example: true
        total:
          type: integer
          description: >-
            Total rows matching the filter. Present only when the request set
            `includeTotal=true`.
          example: 1247
      required:
        - data
        - nextCursor
        - hasMore
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
              description: >-
                Human-readable description of what went wrong. Not stable — do
                not parse.
              example: No merge queue found with ID xK9mP2nQ
          required:
            - code
            - message
      required:
        - error
      description: >-
        The uniform error envelope. Every 4xx and 5xx response in this API has
        this shape.
    TestCaseSummary:
      type: object
      properties:
        id:
          type: string
          description: >-
            The test case's globally unique id. Stable across repos and
            collections, and safe to store.
          example: bfeebcf4-0000-8000-8000-000000000001
        testCollectionId:
          type: string
          description: The owning collection's id.
          example: aB3xY9kQ
        repoId:
          type: string
          description: >-
            The repository the test was uploaded for. The nil UUID for uploads
            made without a repository.
          example: 7a2b3c4d-5e6f-4a1b-8c9d-0e1f2a3b4c5d
        name:
          type: string
          example: suite::test_case_name
        parentName:
          type: string
          example: Foo
        className:
          type: string
          example: FooSuite
        file:
          type: string
          example: src/foo.test.ts
        target:
          type: string
          description: The build target that ran the test, when one was reported.
          example: //src:foo_test
        variant:
          type: string
          example: ''
        crossVariantId:
          type:
            - string
            - 'null'
          description: Groups the same test across variants, when it has any.
          example: 5d4c3b2a-1f0e-4a1b-8c9d-0e1f2a3b4c5d
        codeowners:
          type: array
          items:
            type: string
          maxItems: 1000
          example:
            - '@team-a'
        status:
          $ref: '#/components/schemas/TestStatus'
        quarantineState:
          $ref: '#/components/schemas/QuarantineState'
        labels:
          type: array
          items:
            $ref: '#/components/schemas/Label'
          maxItems: 100
        metrics:
          anyOf:
            - $ref: '#/components/schemas/TestCaseMetrics'
            - type: 'null'
          description: >-
            Returned only when `include=metrics` is set. Null when the test had
            no runs in `period`.
      required:
        - id
        - testCollectionId
        - repoId
        - name
        - parentName
        - className
        - file
        - target
        - variant
        - crossVariantId
        - codeowners
        - status
        - quarantineState
        - labels
    ErrorCode:
      type: string
      enum:
        - AUTHENTICATION_REQUIRED
        - INVALID_TOKEN
        - INSUFFICIENT_PERMISSIONS
        - INTERNAL_ERROR
        - VALIDATION_FAILED
        - NOT_FOUND
        - INVALID_CURSOR
        - MERGE_QUEUE_NOT_FOUND
        - PR_ALREADY_ENQUEUED
        - PR_NOT_ENQUEUEABLE
        - UPSTREAM_ERROR
        - VERIFICATION_RUN_NOT_FOUND
        - PR_NOT_FOUND
        - REPOSITORY_NOT_FOUND
        - IMPACTED_TARGETS_UNSUPPORTED
        - QUEUE_ALREADY_EXISTS
        - QUEUE_HAS_ACTIVE_ENTRIES
        - MODE_CHANGE_IN_PROGRESS
        - PR_NOT_IN_ACTIVE_STATE
        - MERGE_QUEUE_NOT_CREATABLE
        - PR_TESTS_NOT_RESTARTABLE
        - PROVIDER_URL_NOT_FOUND
        - CI_HOST_UNSUPPORTED
        - CI_SCOPE_NOT_FOUND
        - TEST_COLLECTION_NOT_FOUND
        - TEST_CASE_NOT_FOUND
        - RATE_LIMIT_EXCEEDED
      description: >-
        Stable machine-readable error identifier. Switch on this, never on
        `message`. A shipped code's meaning never changes, but **new codes may
        be added within v2** — treat an unrecognized code as a generic failure
        of its HTTP status rather than failing closed.
      example: MERGE_QUEUE_NOT_FOUND
    TestStatus:
      type: string
      enum:
        - healthy
        - flaky
        - broken
      description: >-
        A test's current composite status, reconciled across every active
        monitor.
      example: flaky
    QuarantineState:
      type: string
      enum:
        - quarantined
        - notQuarantined
      description: >-
        Whether the test is currently quarantined, derived from its status, the
        collection's quarantine settings and any override.
      example: notQuarantined
    Label:
      type: object
      properties:
        id:
          type: string
          example: 2b3c4d5e-6f70-4a1b-8c9d-0e1f2a3b4c5d
        name:
          type: string
          example: slow-on-prs
        color:
          type: string
          example: '#4F46E5'
        description:
          type: string
          example: Tests that only get slow on pull-request runs.
        createdAt:
          type: string
          format: date-time
          example: '2026-05-01T00:00:00.000Z'
      required:
        - id
        - name
        - color
        - description
        - createdAt
    TestCaseMetrics:
      type: object
      properties:
        failureRate:
          type: number
          description: Failing runs over total runs in the period, 0 to 1.
          example: 0.25
        prsImpacted:
          type: integer
          description: >-
            Distinct pull requests that saw this test fail while it was
            unhealthy and not quarantined.
          example: 3
        runCount:
          type: integer
          example: 40
        failureCount:
          type: integer
          example: 10
        durationP95Ms:
          type:
            - integer
            - 'null'
          description: >-
            95th-percentile duration in milliseconds. Null when nothing
            measurable ran in the period — skipped runs are not measured.
          example: 1200
        lastRunAt:
          type:
            - string
            - 'null'
          format: date-time
          example: '2026-06-02T10:00:00.000Z'
        lastFailureAt:
          type:
            - string
            - 'null'
          format: date-time
          description: Within the period, unlike the test case's own field.
          example: '2026-06-01T10:00:00.000Z'
      required:
        - failureRate
        - prsImpacted
        - runCount
        - failureCount
        - durationP95Ms
        - lastRunAt
        - lastFailureAt
  securitySchemes:
    orgKey:
      type: http
      scheme: bearer
      description: >-
        An org-scoped API key, sent as `Authorization: Bearer <key>`. Existing
        v1 tokens work here unchanged. There is no enqueue-only or read/write
        scope: an org key authorized for merge-queue writes can invoke every
        merge-queue mutation, including changing merge-protection configuration
        and pausing, draining or deleting a queue.
    trunkToken:
      type: apiKey
      in: header
      name: x-trunk-token
      description: >-
        A short-lived first-party token, sent in the `x-trunk-token` header.
        Obtain one through the CLI device-authentication flow, or by exchanging
        an org API key (`POST /v2/auth/api-key/login`).


        **A token exchanged from an org API key reads merge queues but cannot
        mutate them.** A merge-queue mutation forwards the org API key itself to
        the merge service, and an exchanged token carries no copy of it, so
        those requests answer `403 INSUFFICIENT_PERMISSIONS`. A machine caller
        that mutates merge queues should send the org API key directly as
        `Authorization: Bearer <key>` rather than exchanging it. Tokens from the
        device flow are unaffected.

````