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

# Set or clear paths filters on jobs

> Requires the **Update Dynamic CI settings** permission.

Sets a paths filter on each job named in `filters`, and removes it from each job in `clear`. Name a job by its workflow file and its key under `jobs:`. Each filter applies to pull requests and the merge queue alike. A request names at most 500 jobs in all, across every `filters[].jobs` and `clear`.

It changes only the jobs it names. It never deletes a filter; one the request leaves with no jobs stays in the list, with no jobs. Sending the same body again changes nothing, so no `Idempotency-Key` is needed. A job that already has a different filter fails the request with `PATHS_FILTER_CONFLICT` unless `overwrite` is true. Any error means nothing was written.



## OpenAPI

````yaml /openapi-v2.json patch /v2/dynamic-ci/paths-filters
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: testInvestigations
    x-group: Test Investigations
  - name: testUploads
    x-group: Test Uploads
  - name: detectionEvents
    x-group: Detection Events
paths:
  /v2/dynamic-ci/paths-filters:
    patch:
      tags:
        - dynamicCi
      summary: Set or clear paths filters on jobs
      description: >-
        Requires the **Update Dynamic CI settings** permission.


        Sets a paths filter on each job named in `filters`, and removes it from
        each job in `clear`. Name a job by its workflow file and its key under
        `jobs:`. Each filter applies to pull requests and the merge queue alike.
        A request names at most 500 jobs in all, across every `filters[].jobs`
        and `clear`.


        It changes only the jobs it names. It never deletes a filter; one the
        request leaves with no jobs stays in the list, with no jobs. Sending the
        same body again changes nothing, so no `Idempotency-Key` is needed. A
        job that already has a different filter fails the request with
        `PATHS_FILTER_CONFLICT` unless `overwrite` is true. Any error means
        nothing was written.
      operationId: dynamicCi.setPathsFilters
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPathsFiltersRequest'
      responses:
        '200':
          description: What the request did to each job
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SetPathsFiltersResult'
        '400':
          description: '`VALIDATION_FAILED` | `CI_HOST_UNSUPPORTED`'
          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'
                CI_HOST_UNSUPPORTED:
                  summary: CI_HOST_UNSUPPORTED
                  value:
                    error:
                      code: CI_HOST_UNSUPPORTED
                      message: >-
                        Dynamic CI does not support the repository host
                        gitlab.example.com
        '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
        '404':
          description: '`REPOSITORY_NOT_FOUND` | `CI_SCOPE_NOT_FOUND`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                REPOSITORY_NOT_FOUND:
                  summary: REPOSITORY_NOT_FOUND
                  value:
                    error:
                      code: REPOSITORY_NOT_FOUND
                      message: Repository not found
                CI_SCOPE_NOT_FOUND:
                  summary: CI_SCOPE_NOT_FOUND
                  value:
                    error:
                      code: CI_SCOPE_NOT_FOUND
                      message: >-
                        A workflow or job in the request does not belong to this
                        repository
        '409':
          description: '`PATHS_FILTER_CONFLICT`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                PATHS_FILTER_CONFLICT:
                  summary: PATHS_FILTER_CONFLICT
                  value:
                    error:
                      code: PATHS_FILTER_CONFLICT
                      message: >-
                        1 job already has a different paths filter:
                        .github/workflows/ci.yml test (job
                        3c4f8e21-6a1d-4b7e-9f02-5d8c1a2b3e4f, "backend"). Send
                        "overwrite": true to replace them.
        '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:
    SetPathsFiltersRequest:
      type: object
      properties:
        repo:
          $ref: '#/components/schemas/PlanRepo'
        overwrite:
          type: boolean
          default: false
          description: >-
            Replace a different filter already on a job. Without it, such a job
            fails the request with `PATHS_FILTER_CONFLICT`.
          example: false
        filters:
          type: array
          items:
            $ref: '#/components/schemas/PathsFilterEntry'
          maxItems: 100
        clear:
          type: array
          items:
            $ref: '#/components/schemas/JobRef'
          maxItems: 500
          description: Jobs to remove the paths filter from.
      required:
        - repo
      additionalProperties: false
    SetPathsFiltersResult:
      type: object
      properties:
        filters:
          type: array
          items:
            $ref: '#/components/schemas/WrittenFilter'
          maxItems: 100
          description: >-
            One per `filters` entry, leaving out an entry none of whose jobs
            resolved.
        jobs:
          type: array
          items:
            $ref: '#/components/schemas/WrittenJob'
          maxItems: 500
          description: >-
            One per job named, in request order. A job named by `jobId` is
            reported by its path and key.
      required:
        - filters
        - jobs
    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.
    PlanRepo:
      type: object
      properties:
        host:
          type: string
          default: github.com
          description: >-
            Disambiguates GitHub.com from a GitHub Enterprise Server host.
            `github.com` is the only supported value today.
          example: github.com
        owner:
          type: string
          example: acme
        name:
          type: string
          example: widgets
      required:
        - owner
        - name
    PathsFilterEntry:
      type: object
      properties:
        name:
          type:
            - string
            - 'null'
          maxLength: 80
          description: >-
            Trimmed; blank or absent is unnamed. Names a new filter, or an
            existing unnamed one with the same rules; an existing name is kept.
          example: web
        rules:
          $ref: '#/components/schemas/PathsFilterRulesInput'
        jobs:
          type: array
          items:
            $ref: '#/components/schemas/JobRef'
          minItems: 1
          maxItems: 500
      required:
        - rules
        - jobs
      additionalProperties: false
    JobRef:
      anyOf:
        - $ref: '#/components/schemas/JobRefByName'
        - $ref: '#/components/schemas/JobRefById'
      description: A job, as `{ workflowPath, jobKey }` or as `{ jobId }`.
    WrittenFilter:
      type: object
      properties:
        pathsFilterId:
          type: string
          example: e8f1c2a4-5b6d-4e7f-8a9b-0c1d2e3f4a5b
        name:
          type:
            - string
            - 'null'
          description: The stored name, which an existing named filter keeps.
          example: web
        status:
          $ref: '#/components/schemas/WrittenFilterStatus'
        jobCount:
          type: integer
          description: Distinct jobs on the filter after the write.
          example: 1
      required:
        - pathsFilterId
        - name
        - status
        - jobCount
    WrittenJob:
      type: object
      properties:
        workflowPath:
          type:
            - string
            - 'null'
          example: .github/workflows/ci.yaml
        jobKey:
          type:
            - string
            - 'null'
          example: web-unit
        status:
          $ref: '#/components/schemas/WrittenJobStatus'
        jobIds:
          type: array
          items:
            type: string
          maxItems: 500
          description: >-
            Every job the name matched; one name can match more than one. Empty
            for `unresolved`.
          example:
            - 3c4d0b17-8e2a-4f6c-9d1b-7a5e3c2f1d0e
      required:
        - workflowPath
        - jobKey
        - status
        - jobIds
    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
        - PR_RERUN_FEATURE_DISABLED
        - PR_RERUN_NOT_PENDING_FAILURE
        - PR_RERUN_QUEUE_DOES_NOT_KEEP_FAILED_RUNS
        - PR_RERUN_NOT_A_REQUIRED_CHECK_FAILURE
        - PR_RERUN_CHECK_NOT_RERUNNABLE
        - PR_RERUN_RUN_TORN_DOWN
        - PR_RERUN_HOLD_LIMIT_REACHED
        - PR_RERUN_FORGE_NOT_SUPPORTED
        - PROVIDER_URL_NOT_FOUND
        - CI_HOST_UNSUPPORTED
        - CI_SCOPE_NOT_FOUND
        - PATHS_FILTER_CONFLICT
        - CI_JOB_NOT_FOUND
        - CI_FAILURE_CLUSTER_NOT_FOUND
        - TEST_COLLECTION_NOT_FOUND
        - TEST_NOT_FOUND
        - TEST_UPLOAD_NOT_FOUND
        - INVESTIGATION_NOT_FOUND
        - OUT_OF_RANGE
        - TEST_UNAVAILABLE
        - TICKETING_NOT_CONFIGURED
        - TICKETING_CREDENTIALS_INVALID
        - TICKETING_SITE_REFUSED
        - TICKET_NOT_FOUND
        - TICKET_NOT_LINKED
        - 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
    PathsFilterRulesInput:
      type: object
      properties:
        include:
          type: array
          items:
            $ref: '#/components/schemas/PathsFilterRule'
          minItems: 1
          maxItems: 64
          example:
            - glob: web/**
        exclude:
          type: array
          items:
            $ref: '#/components/schemas/PathsFilterRule'
          maxItems: 64
          description: Defaults to none.
          example:
            - glob: web/**/*.md
      required:
        - include
      additionalProperties: false
    JobRefByName:
      type: object
      properties:
        workflowPath:
          type: string
          minLength: 1
          description: The workflow file's repository-relative path, exactly.
          example: .github/workflows/ci.yaml
        jobKey:
          type: string
          minLength: 1
          description: The job's key under `jobs:` in that file.
          example: web-unit
      required:
        - workflowPath
        - jobKey
      additionalProperties: false
    JobRefById:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
          description: >-
            A job's id from `dynamicCi.listPathsFilters` or
            `dynamicCi.listPrJobs`. A `workflowPath` or `jobKey` sent beside it
            must name the same job.
          example: 3c4f8e21-6a1d-4b7e-9f02-5d8c1a2b3e4f
        workflowPath:
          type:
            - string
            - 'null'
          minLength: 1
          description: The workflow file's repository-relative path, exactly.
          example: .github/workflows/ci.yaml
        jobKey:
          type:
            - string
            - 'null'
          minLength: 1
          description: The job's key under `jobs:` in that file.
          example: web-unit
      required:
        - jobId
      additionalProperties: false
    WrittenFilterStatus:
      type: string
      enum:
        - new
        - existing
      description: Whether the request created the filter or found it by its rules.
      example: new
    WrittenJobStatus:
      type: string
      enum:
        - set
        - unchanged
        - cleared
        - unresolved
      description: >-
        `set`: the job now has the filter. `unchanged`: nothing was written.
        `cleared`: its filter was removed. `unresolved`: Trunk has not seen the
        job run, so nothing was written; send the request again after it runs.
      example: set
    PathsFilterRule:
      type: object
      properties:
        glob:
          type: string
          minLength: 1
          maxLength: 256
          description: >-
            A picomatch glob, matched with `dot: true` against the
            repository-relative path. May not begin with `!` or `(`.
          example: web/**
        changeTypes:
          type: array
          items:
            $ref: '#/components/schemas/ChangeType'
          minItems: 1
          maxItems: 6
          description: Match only these kinds of change. Absent matches any.
          example:
            - added
            - modified
      required:
        - glob
      additionalProperties: false
    ChangeType:
      type: string
      enum:
        - added
        - copied
        - deleted
        - modified
        - renamed
        - unmerged
      description: A kind of file change, as `dorny/paths-filter` names them.
      example: modified
  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.

````