> ## 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 tests

> Lists tests, 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 for those.



## OpenAPI

````yaml /openapi-v2.json get /v2/tests
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


    For automation, send an API key as `Authorization: Bearer <key>`. An
    organization admin creates one in the Trunk app under **Settings → Developer
    → API Keys**, and chooses the permissions it holds. A key with no
    permissions can read merge queues but change nothing in them. Each write's
    description names the permission it needs, if any. Permissions cover merge
    queues and test-result uploads, including the quarantine lookups a CI run
    makes before it uploads; any key can call the other Flaky Tests and Dynamic
    CI endpoints, including their writes.


    A short-lived token in the `x-trunk-token` header is what Trunk's own tools
    send, such as the `trunk` CLI after `trunk auth login`. Your own
    integrations should use an API key.


    A GitHub Actions job can upload impacted targets with no Trunk secret: the
    `trunk-io/login` action exchanges the run's own GitHub credential (its OIDC
    token, or on a pull request from a fork its `GITHUB_TOKEN`) for a
    short-lived first-party token. That token is accepted only by `PUT
    /v2/impacted-targets`, for the run's own repository — and on a fork pull
    request, only for that pull request and its head sha.


    ## Errors


    Errors from the API are an `ErrorResponse`: `{ "error": { "code", "message"
    } }`. Switch on `code`, which is stable; `message` is prose and may change
    at any time.


    Some responses don't have this shape. Trunk's edge answers `401`, `403` for
    some credential problems (including a request with no credential), `429`,
    and gateway errors such as `503` before the request reaches the API. The API
    itself answers `413` for a body over 512 KB and `504` for a request that
    runs past its time limit (usually 5 seconds), both as plain text. Read
    `error.code` only when the body is JSON with an `error` object, and
    otherwise branch on the status code.


    **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 from the API carries an `X-Request-Id` header; edge responses
    may not. Quote it in support requests.


    ## Rate limiting


    Most requests share one limit per organization (currently 100 requests per
    second), across every API key, user, and CLI session in it. Some requests
    have limits of their own and don't count toward it: `GET` requests on paths
    that start with `/v2/pull-requests`, `/v2/ci-jobs`, `/v2/ci-workflow-runs`,
    or `/v2/ci-failure-clusters` (5 per second per API key or user); the merge
    queue metrics endpoints (5 per second per organization); impacted-target
    uploads from a GitHub Actions login (10 per second per repository, and
    separately for its fork pull requests); test-result uploads (1000 per second
    per organization) and quarantine lookups (250 per second per organization).
    Exceeding a limit returns `429`; retry with backoff. The limit is enforced
    at the edge, which does not yet wrap its `429` in the `ErrorResponse`
    envelope or send `Retry-After`, so 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. `PUT
    /v2/impacted-targets` accepts GitHub and GitLab repositories. A GitHub
    Actions login (the `trunk-io/login` action, or `trunk auth login
    --github-actions`) uploads only for its own GitHub repository. The
    `Provider` enum lists values the repository index can store, not surfaces
    that are fully supported.


    ## Not yet available


    A pull request's numeric **position** in its queue is not exposed, and list
    responses are not ordered by queue position. `GET
    /v2/merge-queues/{id}/order` returns the order the engine will consider pull
    requests in, but an array index there is not a wait: use a pending entry's
    `directlyAhead` rather than counting, and `GET
    /v2/pull-requests/{id}/queue-entry-estimate` for when it is expected to
    merge (on queues that serve estimates). This API delivers no events of its
    own — poll it, or subscribe to [Merge Queue's
    webhooks](https://docs.trunk.io/merge-queue/webhooks). `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:
  - apiKey: []
  - 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: ciJobs
    x-group: CI Jobs
  - name: ciWorkflowRuns
    x-group: CI Workflow Runs
  - name: ciFailureClusters
    x-group: CI Failure Clusters
  - name: testCollections
    x-group: Test Collections
  - name: tests
    x-group: Tests
  - name: testUploads
    x-group: Test Uploads
  - name: detectionEvents
    x-group: Detection Events
paths:
  /v2/tests:
    get:
      tags:
        - tests
      summary: List and search tests
      description: >-
        Lists tests, 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 for those.
      operationId: tests.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 tests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestList'
        '400':
          description: '`VALIDATION_FAILED` | `INVALID_CURSOR`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                VALIDATION_FAILED:
                  summary: VALIDATION_FAILED
                  value:
                    error:
                      code: VALIDATION_FAILED
                      message: 'Invalid input: expected string, received undefined'
                INVALID_CURSOR:
                  summary: INVALID_CURSOR
                  value:
                    error:
                      code: INVALID_CURSOR
                      message: Invalid or expired cursor.
        '401':
          description: >-
            `AUTHENTICATION_REQUIRED` | `INVALID_TOKEN`. A malformed, expired,
            or deleted credential. Trunk's edge usually answers this before the
            request reaches the API, with a body that is not this envelope
            (`{"error": "unauthorized: invalid api key"}` for a bad API key, or
            plain text for a bad `x-trunk-token`), so branch on the status. A
            request with no credential at all gets `403` from the edge.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                AUTHENTICATION_REQUIRED:
                  summary: AUTHENTICATION_REQUIRED
                  value:
                    error:
                      code: AUTHENTICATION_REQUIRED
                      message: No credential was supplied
                INVALID_TOKEN:
                  summary: INVALID_TOKEN
                  value:
                    error:
                      code: INVALID_TOKEN
                      message: The supplied token failed verification
        '403':
          description: '`INSUFFICIENT_PERMISSIONS`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                INSUFFICIENT_PERMISSIONS:
                  summary: INSUFFICIENT_PERMISSIONS
                  value:
                    error:
                      code: INSUFFICIENT_PERMISSIONS
                      message: >-
                        The supplied credential is missing the required
                        permission
        '429':
          description: >-
            `RATE_LIMIT_EXCEEDED`. Trunk's edge answers this, and does not yet
            wrap it in the error envelope or send `Retry-After`: treat any `429`
            as rate limiting, whatever its body, and retry with backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                RATE_LIMIT_EXCEEDED:
                  summary: RATE_LIMIT_EXCEEDED
                  value:
                    error:
                      code: RATE_LIMIT_EXCEEDED
                      message: Rate limit exceeded; retry with backoff
        '500':
          description: '`INTERNAL_ERROR`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                INTERNAL_ERROR:
                  summary: INTERNAL_ERROR
                  value:
                    error:
                      code: INTERNAL_ERROR
                      message: An unexpected error occurred
components:
  schemas:
    TestList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TestSummary'
          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: An unexpected error occurred
          required:
            - code
            - message
      required:
        - error
      description: >-
        The error envelope. Every error this API itself returns has this shape,
        except a `413` for a body over 512 KB and a `504` for a request that
        runs past its time limit, which are plain text. Responses from Trunk's
        edge, such as a `401`, a `403` for a missing credential, a `429` or a
        `503`, can arrive before the request reaches the API and may not have
        it, so read `error.code` only when the body is JSON with an `error`
        object.
    TestSummary:
      type: object
      properties:
        id:
          type: string
          description: >-
            The test'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
        repositoryId:
          type: string
          description: >-
            The repository the test was uploaded for. Resolve with `GET
            /v2/repositories/{id}`. Uploads made without a repository carry the
            nil UUID, which names no repository and does not resolve.
          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'
        statusSince:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When `status` last changed — how long the test has been in its
            current state. Null for a test no monitor has ever classified.
          example: '2026-06-01T10:00:00.000Z'
        quarantineState:
          $ref: '#/components/schemas/QuarantineState'
        quarantinedSince:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the test's current quarantine began, as far as Trunk records
            it. Null exactly when `quarantineState` is `notQuarantined`. For a
            test quarantined by an `alwaysQuarantine` override, the time that
            override was set. For a test quarantined automatically, the later of
            `statusSince` and the last time an override on it was cleared.
            Changes to the collection's quarantine settings are not recorded per
            test, so this can be earlier than when quarantining was turned on
            for the collection.
          example: '2026-06-01T10:00:00.000Z'
        labels:
          type: array
          items:
            $ref: '#/components/schemas/Label'
          maxItems: 100
        url:
          type: string
          format: uri
          description: The test's page in the Trunk web app.
          example: >-
            https://app.trunk.io/acme/flaky-tests/collections/aB3xY9kQ/tests/bfeebcf4-0000-8000-8000-000000000001
        ticket:
          anyOf:
            - $ref: '#/components/schemas/TicketSummary'
            - type: 'null'
          description: >-
            The ticket currently linked to this test, as Trunk last synced it,
            or null if none is linked.
        metrics:
          anyOf:
            - $ref: '#/components/schemas/TestMetrics'
            - type: 'null'
          description: >-
            Returned only when `include=metrics` is set. Null when the test had
            no runs in `period`.
      required:
        - id
        - testCollectionId
        - repositoryId
        - name
        - parentName
        - className
        - file
        - target
        - variant
        - crossVariantId
        - codeowners
        - status
        - statusSince
        - quarantineState
        - quarantinedSince
        - labels
        - url
        - ticket
    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
        - ALLOWED_BOT_SUBMITTERS_FROZEN
        - ESTIMATE_NOT_ENABLED
        - PROVIDER_URL_NOT_FOUND
        - CI_HOST_UNSUPPORTED
        - CI_SCOPE_NOT_FOUND
        - CI_JOB_NOT_FOUND
        - CI_FAILURE_CLUSTER_NOT_FOUND
        - TEST_COLLECTION_NOT_FOUND
        - TEST_NOT_FOUND
        - TEST_UPLOAD_NOT_FOUND
        - OUT_OF_RANGE
        - TICKETING_NOT_CONFIGURED
        - TICKETING_CREDENTIALS_INVALID
        - TICKETING_SITE_REFUSED
        - TICKET_NOT_FOUND
        - TICKET_LINK_CONFLICT
        - TEST_ALREADY_LINKED
        - TICKET_FIELDS_REJECTED
        - TICKET_CONTENT_UNAVAILABLE
        - TICKETING_CREATE_REFUSED
        - 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: INTERNAL_ERROR
    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
        description:
          type: string
          example: Tests that only get slow on pull-request runs.
        assignedAt:
          type: string
          format: date-time
          description: >-
            The earliest of `sources[].assignedAt`. Exact when a user applied
            the label; see `MonitorLabelSource.assignedAt` for when a monitor
            did.
          example: '2026-05-01T00:00:00.000Z'
        sources:
          type: array
          items:
            $ref: '#/components/schemas/LabelSource'
          maxItems: 100
          description: >-
            Everything that put this label on the test. More than one entry
            means several sources applied the same label independently; removing
            it clears them all.
      required:
        - id
        - name
        - description
        - assignedAt
        - sources
      description: >-
        A label on a test, folded across its sources — one entry per label,
        never one per assignment.
    TicketSummary:
      type: object
      properties:
        id:
          type: string
          description: Trunk's id for the linked ticket.
          example: 5d4c3b2a-1f0e-4a1b-8c9d-0e1f2a3b4c5d
        provider:
          $ref: '#/components/schemas/TicketProvider'
        key:
          type: string
          description: >-
            The provider's human-readable key. Mutable on some providers (a Jira
            issue moved between projects gets a new one).
          example: TRUNK-1234
        url:
          type: string
          format: uri
          example: https://acme.atlassian.net/browse/TRUNK-1234
        status:
          type: string
          description: The provider's status name, as last synced.
          example: In Progress
      required:
        - id
        - provider
        - key
        - url
        - status
      description: >-
        The ticket linked to a test, as Trunk last synced it. Reading a test
        never calls the ticketing provider.
    TestMetrics:
      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's own field.
          example: '2026-06-01T10:00:00.000Z'
      required:
        - failureRate
        - prsImpacted
        - runCount
        - failureCount
        - durationP95Ms
        - lastRunAt
        - lastFailureAt
    LabelSource:
      oneOf:
        - $ref: '#/components/schemas/UserLabelSource'
        - $ref: '#/components/schemas/MonitorLabelSource'
      discriminator:
        propertyName: type
        mapping:
          user: '#/components/schemas/UserLabelSource'
          monitor: '#/components/schemas/MonitorLabelSource'
      description: >-
        Something that put a label on a test. A `user` source carries no monitor
        fields and a `monitor` source carries no user.
    TicketProvider:
      type: string
      enum:
        - jiraCloud
        - linear
        - asana
        - jiraDataCenter
      description: The ticketing system a ticket lives in.
      example: jiraCloud
    UserLabelSource:
      type: object
      properties:
        type:
          type: string
          enum:
            - user
        user:
          anyOf:
            - $ref: '#/components/schemas/LabelUser'
            - type: 'null'
          description: >-
            Who applied the label. Null when an API token did — a token resolves
            to no user.
        assignedAt:
          type: string
          format: date-time
          description: When the user applied the label.
          example: '2026-06-01T00:00:00.000Z'
      required:
        - type
        - user
        - assignedAt
      description: A label applied by hand.
    MonitorLabelSource:
      type: object
      properties:
        type:
          type: string
          enum:
            - monitor
        monitorId:
          type: string
          example: 1a2b3c4d-5e6f-4a1b-8c9d-0e1f2a3b4c5d
        monitorType:
          $ref: '#/components/schemas/LabelingMonitorType'
        assignedAt:
          type: string
          format: date-time
          description: >-
            The monitor has applied this label since at least this time. Muting
            the monitor, then unmuting it, and editing its label action all
            re-create the assignment, so this can be later than the first time
            it applied the label.
          example: '2026-06-01T00:00:00.000Z'
      required:
        - type
        - monitorId
        - monitorType
        - assignedAt
      description: A label applied by a monitor.
    LabelUser:
      type: object
      properties:
        id:
          type: string
          example: 6f708192-a3b4-4c5d-8e9f-0a1b2c3d4e5f
        name:
          type:
            - string
            - 'null'
          description: >-
            Null only if the user record could not be resolved; the id still
            identifies them.
          example: Ada Lovelace
      required:
        - id
        - name
      description: The person who applied a label.
    LabelingMonitorType:
      type: string
      enum:
        - failureRate
        - passOnRetry
        - failureCount
        - newTest
        - skippedTest
        - slowTest
        - timeoutInflation
      description: A monitor type that can apply a label.
      example: failureRate
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        An API key, sent as `Authorization: Bearer <key>`. An organization admin
        creates one in the Trunk app under **Settings → Developer → API Keys**
        and chooses the permissions it holds. A key with none can read merge
        queues but change nothing in them. Permissions cover merge queues and
        test-result uploads, including the quarantine lookups a CI run makes
        before it uploads; any key can call the other Flaky Tests and Dynamic CI
        endpoints, including their writes.
    trunkToken:
      type: apiKey
      in: header
      name: x-trunk-token
      description: >-
        A short-lived token that Trunk's own tools send in the `x-trunk-token`
        header: the `trunk` CLI after `trunk auth login`, and the
        `trunk-io/login` action in a GitHub Actions run. Your own integrations
        should use an API key instead. Never send `Authorization` and
        `x-trunk-token` on the same request.


        **A token from a GitHub Actions run can only upload impacted targets**
        (`PUT /v2/impacted-targets`), for the repository the run belongs to;
        every other endpoint refuses it.

````