Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,89 @@
}
]
},
{
"tab": "Merge Queue API v2",
"hidden": true,
"groups": [
{
"group": "Merge Queue API (v2)",
"root": "merge-queue/reference/api-v2",
"openapi": {
"source": "/openapi-v2.json",
"directory": "/merge-queue/reference/api-v2"
},
"pages": [
"merge-queue/reference/api-v2/migrate-from-v1",
{
"group": "Merge queues",
"pages": [
"GET /v2/merge-queues",
"POST /v2/merge-queues",
"GET /v2/merge-queues/{id}",
"PATCH /v2/merge-queues/{id}/config",
"POST /v2/merge-queues/{id}/switch-modes",
"POST /v2/merge-queues/{id}/pause",
"POST /v2/merge-queues/{id}/resume",
"POST /v2/merge-queues/{id}/drain",
"DELETE /v2/merge-queues/{id}"
]
},
{
"group": "Queue order",
"pages": [
"GET /v2/merge-queues/{id}/order",
"GET /v2/merge-queues/{id}/graph"
]
},
{
"group": "Pull requests",
"pages": [
"POST /v2/merge-queues/{id}/pull-requests",
"GET /v2/merge-queues/{id}/pull-requests",
"GET /v2/pull-requests",
"GET /v2/pull-requests/{id}",
"POST /v2/pull-requests/{id}/reprioritize",
"PATCH /v2/pull-requests/{id}/queue-entry",
"POST /v2/pull-requests/{id}/restart-tests",
"POST /v2/pull-requests/{id}/dequeue",
"GET /v2/pull-requests/{id}/queue-entry-history",
"GET /v2/queue-entry-history"
]
},
{
"group": "Testing",
"pages": [
"GET /v2/merge-queues/{id}/verification-runs",
"GET /v2/pull-requests/{id}/verification-runs",
"GET /v2/merge-queue-verification-runs/{id}",
"GET /v2/merge-queues/{id}/testing-results"
]
},
{
"group": "Impacted targets",
"pages": [
"PUT /v2/impacted-targets",
"merge-queue/reference/api-v2/github-actions"
]
},
{
"group": "Metrics",
"pages": [
"GET /v2/merge-queues/metrics",
"GET /v2/merge-queues/{id}/metrics"
]
},
{
"group": "Repositories",
"pages": [
"GET /v2/repositories",
"GET /v2/repositories/{id}"
]
}
]
}
]
},
{
"tab": "Changelog",
"hidden": true,
Expand Down
196 changes: 196 additions & 0 deletions merge-queue/reference/api-v2.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
---
title: "Merge Queue API (v2)"
description: "REST API for merge queues, the pull requests in them, and the testing they run."
og:title: "Trunk Merge Queue API v2 reference"
---
The v2 Merge Queue API lets you manage merge queues and the pull requests in them, read the order the queue will test them in, and inspect every verification run. It is an HTTP REST API hosted at `https://api.trunk.io/v2` that returns JSON and uses standard HTTP status codes.

v2 addresses everything by ID rather than by repository and branch coordinates, pages every list the same way, and returns one error shape. It sits alongside the [v1 API](/merge-queue/reference/merge), which keeps working unchanged. To move existing automation or dashboards to v2, see [Migrate from the v1 API](/merge-queue/reference/api-v2/migrate-from-v1).

## Authentication

Authenticate automation with an API key, sent as `Authorization: Bearer <key>`:

```bash
curl https://api.trunk.io/v2/merge-queues \
-H "Authorization: Bearer $TRUNK_API_KEY"
```

To upload impacted targets from CI, you don't need an API key at all: a GitHub Actions job can [log in with the run's own GitHub credential](/merge-queue/reference/api-v2/github-actions), pull requests from forks included.

### Create an API key

An organization admin creates API keys in the [Trunk app](https://app.trunk.io):

1. Go to **Settings** → **Developer** → **API Keys** and click **Create Key**.
2. Name the key after what will use it, and choose the permissions it holds.
3. Copy the key. It is shown only once — if you lose it, revoke it and create another.

You can change a key's permissions later, and the change applies to the next request that uses it.

### Permissions

A key with no permissions can read merge queues but change nothing. Grant only what the automation needs:

| Permission | Allows |
| --- | --- |
| **Merge and cancel PRs** | Submit pull requests to the queue, restart their tests, remove any entry from it, and upload impacted targets from CI. |
| **Set priority on PRs** | Move a pull request ahead of others in the queue. |
| **Skip batching** | Opt a pull request out of batching so it gets a test run of its own. |
| **Merge PRs with direct to queue** | Send a pull request to the queue before branch protection is satisfied. The queue's own tests still run and still gate the merge. |
| **Update Merge Queue settings** | Change queue settings; pause, resume, or drain a queue; and create or delete queues. |

A request the key isn't permitted returns `403 INSUFFICIENT_PERMISSIONS`.

<Note>
These permissions apply to merge queues only. Every API key can use the Flaky Tests and Dynamic CI endpoints, including the ones that make changes, whatever permissions it holds.
</Note>

## Finding IDs

Every merge queue, pull request, repository, and verification run has an `id`, and endpoints address resources by it. Resolve IDs once and reuse them:

1. **Queue:** [`GET /v2/merge-queues`](/merge-queue/reference/api-v2/mergequeues/list-merge-queues) needs no arguments and returns every queue in your organization, each with its `id`, `repositoryId`, `repositoryUrl`, and `targetBranch`. To find one repository's queues, first resolve the repository with [`GET /v2/repositories?providerUrl=…`](/merge-queue/reference/api-v2/repositories/resolve-a-repository-by-its-provider-url).
2. **Pull request:** [`GET /v2/pull-requests?repositoryId=…&number=…`](/merge-queue/reference/api-v2/pullrequests/resolve-pull-requests-by-repository-coordinates) resolves up to 50 pull request numbers at once, or a head `branch`.

```bash
curl "https://api.trunk.io/v2/pull-requests?repositoryId=7c9e6679-7425-40de-944b-e07fc1f90ae7&number=4901" \
-H "Authorization: Bearer $TRUNK_API_KEY"
```

Submitting a pull request is the exception: it takes the pull request's `number` in the body, so you only need the queue's ID.

```bash
curl -X POST https://api.trunk.io/v2/merge-queues/xK9mP2nQ/pull-requests \
-H "Authorization: Bearer $TRUNK_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "number": 4901 }'
```

## Pagination

Every list endpoint returns the same envelope:

```json
{
"data": [],
"nextCursor": "AQEKZm9vYmFy",
"hasMore": true
}
```

Pass `nextCursor` back verbatim as `cursor` to get the next page, until `hasMore` is `false`. Cursors are opaque, forward-only, and bound to the endpoint and filters they came from. `limit` sets the page size (1–100). Set `includeTotal=true` to add a `total` count, which costs an extra query and is off by default.

**List order is not queue order.** A queue's pull request list is most recently enqueued first. For the order the queue will test pull requests in, use [`GET /v2/merge-queues/{id}/order`](/merge-queue/reference/api-v2/mergequeues/get-a-merge-queues-dispatch-order).

## Errors

Every 4xx and 5xx response has the same shape:

```json
{
"error": {
"code": "MERGE_QUEUE_NOT_FOUND",
"message": "No merge queue found with ID xK9mP2nQ"
}
}
```

Switch on `code`, which is stable. `message` is for people and may change at any time.

- **`404` also means "not yours".** A read of a resource in another organization returns `404`, not `403`, so the API never reveals whether it exists.
- **`403 INSUFFICIENT_PERMISSIONS`** means your credential is valid but not permitted to take that action.
- **`429`** means you exceeded the rate limit of 100 requests per second per organization. Retry with backoff. A `429` may not carry the error envelope, so treat any `429` as rate limiting.

Every response carries an `X-Request-Id` header. Include it when you contact support.

## Retrying writes

`Idempotency-Key` is not honored yet. If a write times out or its outcome is unknown, read the resource back before retrying — a blind retry can, for example, restart a pull request's tests twice. Each write's reference page describes how to reconcile it.

## Compatibility

Within v2, Trunk may add endpoints, optional request parameters, optional response fields, and new enum values — including new error codes and queue-entry states. **Tolerate values and fields you don't recognize.** A client that switches exhaustively over an enum will break when a member is added. Removals and changes in meaning require a new major version.

## Limitations

- Merge queues, verification runs, and checks are **GitHub-only**. Impacted-target uploads also require a `github.com` repository named `owner/repo`.
- The API has no webhooks of its own: monitor it by polling, or use [Merge Queue webhooks](/merge-queue/webhooks).

## Endpoints

### Merge queues

<Columns cols={2}>
<Card title="List merge queues" href="/merge-queue/reference/api-v2/mergequeues/list-merge-queues" />
<Card title="Create a merge queue" href="/merge-queue/reference/api-v2/mergequeues/create-a-merge-queue" />
<Card title="Get a merge queue" href="/merge-queue/reference/api-v2/mergequeues/get-a-merge-queue" />
<Card title="Update a merge queue's configuration" href="/merge-queue/reference/api-v2/mergequeues/update-a-merge-queues-configuration" />
<Card title="Switch a merge queue's mode" href="/merge-queue/reference/api-v2/mergequeues/switch-a-merge-queues-mode" />
<Card title="Pause a merge queue" href="/merge-queue/reference/api-v2/mergequeues/pause-a-merge-queue" />
<Card title="Resume a merge queue" href="/merge-queue/reference/api-v2/mergequeues/resume-a-merge-queue" />
<Card title="Drain a merge queue" href="/merge-queue/reference/api-v2/mergequeues/drain-a-merge-queue" />
<Card title="Delete a merge queue" href="/merge-queue/reference/api-v2/mergequeues/delete-a-merge-queue" />
</Columns>

### Queue order

Which pull requests the queue will test next, and which ones it considers related. Use the order to answer "what runs next"; the graph explains why pull requests depend on each other, but not when they run.

<Columns cols={2}>
<Card title="Get a merge queue's dispatch order" href="/merge-queue/reference/api-v2/mergequeues/get-a-merge-queues-dispatch-order" />
<Card title="Get a merge queue's dependency graph" href="/merge-queue/reference/api-v2/mergequeues/get-a-merge-queues-dependency-graph" />
</Columns>

### Pull requests

<Columns cols={2}>
<Card title="Submit a pull request to a merge queue" href="/merge-queue/reference/api-v2/mergequeues/submit-a-pull-request-to-a-merge-queue" />
<Card title="List a merge queue's pull requests" href="/merge-queue/reference/api-v2/mergequeues/list-a-merge-queues-pull-requests" />
<Card title="Resolve pull requests by repository coordinates" href="/merge-queue/reference/api-v2/pullrequests/resolve-pull-requests-by-repository-coordinates" />
<Card title="Get a pull request" href="/merge-queue/reference/api-v2/pullrequests/get-a-pull-request" />
<Card title="Reprioritize a queued pull request" href="/merge-queue/reference/api-v2/pullrequests/reprioritize-a-queued-pull-request" />
<Card title="Update a queued pull request's entry" href="/merge-queue/reference/api-v2/pullrequests/update-a-queued-pull-requests-entry" />
<Card title="Restart a queued pull request's tests" href="/merge-queue/reference/api-v2/pullrequests/restart-a-queued-pull-requests-tests" />
<Card title="Dequeue a pull request" href="/merge-queue/reference/api-v2/pullrequests/dequeue-a-pull-request" />
<Card title="List a pull request's queue-entry state history" href="/merge-queue/reference/api-v2/pullrequests/list-a-pull-requests-queue-entry-state-history" />
<Card title="List queue-entry state history for several pull requests" href="/merge-queue/reference/api-v2/pullrequests/list-queue-entry-state-history-for-several-pull-requests" />
</Columns>

A pull request's queue entry moves through the states `notReady`, `pending`, `testing`, `testsPassed`, `pendingFailure`, and ends in `merged`, `failed`, or `cancelled`. To see why an entry changed state — for example, why it keeps being re-tested — read its queue-entry history and inspect each transition's `reason`.

### Testing

A verification run is one CI run the queue started to test one pull request or a batch of them.

<Columns cols={2}>
<Card title="List a merge queue's verification runs" href="/merge-queue/reference/api-v2/mergequeues/list-a-merge-queues-verification-runs" />
<Card title="List a pull request's verification runs" href="/merge-queue/reference/api-v2/pullrequests/list-a-pull-requests-verification-runs" />
<Card title="Get a verification run" href="/merge-queue/reference/api-v2/mergequeueverificationruns/get-a-verification-run" />
<Card title="List a merge queue's completed testing" href="/merge-queue/reference/api-v2/mergequeues/list-a-merge-queues-completed-testing" />
</Columns>

### Impacted targets

For queues running [parallel queues](/merge-queue/optimizations/parallel-queues), CI uploads the build targets each pull request impacts.

<Columns cols={2}>
<Card title="Upload a pull request's impacted targets" href="/merge-queue/reference/api-v2/impactedtargets/upload-a-pull-requests-impacted-targets" />
<Card title="Upload impacted targets from GitHub Actions" href="/merge-queue/reference/api-v2/github-actions" />
</Columns>

### Metrics

Prometheus text exposition, with the same metric names and labels as v1's [`getMergeQueueMetrics`](/merge-queue/reference/merge/get-prometheus-format-metrics-for-a-merge-queue) — an existing scrape config or dashboard needs only its URL and credential changed. See [Prometheus metrics](/merge-queue/administration/metrics#prometheus-metrics-endpoint) for the metrics and example queries.

<Columns cols={2}>
<Card title="Get Prometheus metrics for every merge queue" href="/merge-queue/reference/api-v2/mergequeues/get-prometheus-metrics-for-every-merge-queue" />
<Card title="Get Prometheus metrics for a merge queue" href="/merge-queue/reference/api-v2/mergequeues/get-prometheus-metrics-for-a-merge-queue" />
</Columns>

### Repositories

<Columns cols={2}>
<Card title="Resolve a repository by its provider URL" href="/merge-queue/reference/api-v2/repositories/resolve-a-repository-by-its-provider-url" />
<Card title="Get a repository" href="/merge-queue/reference/api-v2/repositories/get-a-repository" />
</Columns>
77 changes: 77 additions & 0 deletions merge-queue/reference/api-v2/github-actions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: "Upload impacted targets from GitHub Actions"
description: "Authenticate a GitHub Actions job to the v2 API with no Trunk secret, pull requests from forks included."
og:title: "Upload impacted targets from GitHub Actions with the Trunk Merge Queue API"
---
A GitHub Actions job can [upload a pull request's impacted targets](/merge-queue/reference/api-v2/impactedtargets/upload-a-pull-requests-impacted-targets) without a Trunk API key. The [`trunk-io/login`](https://github.com/trunk-io/login) action exchanges the run's own GitHub credential for a short-lived Trunk login, so there is no secret to create, store, or rotate — and it works on pull requests from forks, where repository secrets are not available.

The login can **only** upload impacted targets, and only for the repository the run belongs to. Every other endpoint refuses it. For anything else, use an [API key](/merge-queue/reference/api-v2#create-an-api-key).

## Set up the workflow

```yaml
on:
pull_request:

permissions:
contents: read
id-token: write # the run's OIDC token, for pull requests from this repository

jobs:
impacted-targets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# ... compute the impacted targets into targets.txt, one per line ...

- uses: trunk-io/login@v1

- run: trunk mergequeue upload-impacted-targets --targets-file targets.txt
env:
GITHUB_TOKEN: ${{ github.token }} # lets a long fork-PR job renew its login
```

`trunk-io/login` installs the `trunk` CLI, puts it on `PATH`, and logs in. On a pull request, `trunk mergequeue upload-impacted-targets` takes the repository, pull request number, head commit, and target branch from the run.

Declare the pull request's impact with one of:

| Option | Uploads |
| --- | --- |
| `--targets-file <file>` | The target names in the file, one per line. A file with no targets is refused, because an empty file is more often a failed computation than a real answer. |
| `--none` | An empty list: the pull request impacts nothing. This still satisfies the queue's impacted-targets check. |
| `--all` | The pull request impacts every target. |

## How the job authenticates

The CLI picks the credential from the run.

| Run | Credential | Needs |
| --- | --- | --- |
| `pull_request` from this repository, `merge_group`, `push`, `workflow_dispatch` | The run's GitHub OIDC token | `permissions: id-token: write` |
| `pull_request` from a fork, `pull_request_target` | The job's `GITHUB_TOKEN`, bound to that pull request and its head commit | **Fork PR CI access** turned on for the repository in Trunk, by an organization admin (**Settings** → **Repositories**) |

- The CLI sends `GITHUB_TOKEN` to Trunk only on a pull request from a fork.
- A fork pull request's login is bound to that pull request and its head commit, and can upload only for them.
- Other events, such as `issue_comment`, `workflow_run`, and `schedule`, are refused: some of them let someone outside the repository run code with the repository's OIDC token.
- GitHub may hold a first-time contributor's run until a maintainer approves it.

## Security

- Nothing is exported to the job's environment or to later steps. The login is saved to the CLI's credential file (mode `0600`) and used only by the upload.
- The login expires after at most 15 minutes. The CLI renews it when it is close to expiring. On a fork pull request, renewing needs `GITHUB_TOKEN` in the upload step's `env`, as in the example above.
- Each login is tied to the run that made it, so a later job on a reused self-hosted runner does not pick it up.

## Action inputs

| Input | Default | Description |
| --- | --- | --- |
| `api-url` | `https://api.trunk.io/v2` | The Trunk API root. |
| `cli-version` | Pinned per release | The `trunk` CLI version to install. |
| `cli-channel` | `prod` | The release channel `cli-version` is published on. |
| `github-token` | `${{ github.token }}` | The job's GitHub token. Sent to Trunk only on a pull request from a fork. |

## Requirements

- Linux (x64 or arm64) or macOS (arm64) runners. Windows is not supported.
- The repository is connected to Trunk: the Trunk GitHub App is installed on it.
Loading
Loading