Skip to main content
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 the CI runs it starts. It is an HTTP REST API that returns JSON and uses standard HTTP status codes. The base URL is https://api.trunk.io. Endpoint paths already include /v2 (for example, /v2/merge-queues), so don’t add it to the host. Use v2 for new integrations. The v1 API keeps working unchanged. To move existing code to v2, see Migrate from the v1 API.

Before you start

Merge queues are GitHub-only. Install the Trunk GitHub App on the repository, and create a merge queue for the branch you merge into. See Install and create your queue, or create the queue with the API.

Concepts

  • Queue entry: a pull request’s place in a merge queue, under queueEntry on a pull request. Its id differs from the pull request’s id. Endpoints under /v2/pull-requests/{id} take the pull request’s id.
  • Queue entry state: notReady (submitted, but waiting on a requirement such as passing checks or impacted targets), pending (ready, waiting to test), testing, testsPassed, and pendingFailure. The final states are merged, failed, and cancelled. queueEntry.readiness explains a notReady entry. A pendingFailure entry’s tests failed, and the queue holds it to learn whether the failure is its own, for example while the pull requests ahead of it finish. See Pending failure depth.
  • Verification run: one CI run the queue started to test a pull request, or a batch of them. When a batch fails, the queue splits it to find the culprit (bisection). Each split is its own verification run.
  • Mode: queue mode (Single in the app) puts pull requests in one line, each tested on top of the ones ahead of it, several at a time. graph mode (Parallel in the app) gives unrelated pull requests separate lines, based on the impacted targets CI uploads for each.
  • Priority: lower priorityValue numbers test first. The names are urgent (0), high (10), medium (100), and low (200). Send a number as priorityValue or a name as priorityName. Without either, a pull request gets medium (100). urgent (or priorityValue: 0) restarts lower-priority pull requests that are already testing, so reserve it for emergencies. See Priority merging.
  • Direct to queue: direct: true sends a pull request to the queue before its branch protection is satisfied. See Direct-to-Queue.

Authentication

Authenticate automation with an API key, sent as Authorization: Bearer <key>:
An organization admin creates keys under Settings → Developer → API Keys. A key with no permissions can read merge queues but can’t change them. The organization API token works only with the v1 API. A GitHub Actions job that uploads impacted targets can authenticate without a key. See Authentication for permissions, rotation, and errors.

Finding IDs

Endpoints address resources by id. Look IDs up once and store them:
  1. Queue: GET /v2/merge-queues lists your organization’s queues, each with its id, repositoryId, repositoryUrl, and targetBranch. To list one repository’s queues, get its ID from GET /v2/repositories?providerUrl=https://github.com/<owner>/<repo> (.data[0].id), then call GET /v2/merge-queues?repositoryId=<id>&targetBranch=<branch>.
  2. Pull request: GET /v2/pull-requests?repositoryId=…&number=… resolves up to 50 comma-separated pull request numbers at once (set limit=50, since the default page is 25), or one head branch. It finds only pull requests Trunk’s GitHub App has already seen. If you just opened one, retry shortly.

Submit and wait for a merge

Submitting needs a key with the Merge and cancel PRs permission. Submit takes the pull request’s number in the body, so it needs only the queue’s ID and has no lookup delay:
Its 201 response is the pull request; keep its id for polling.
  • If the pull request is already queued here (not yet merged, failed, or cancelled), submit returns 409 PR_ALREADY_ENQUEUED and changes nothing. Look up its id instead, using the queue’s repositoryId from GET /v2/merge-queues/{id}.
  • If Trunk can’t enqueue it, submit returns 422 PR_NOT_ENQUEUEABLE with the reason in message. For example:
    • The pull request doesn’t exist.
    • It targets a different branch from the queue’s. A stacked pull request may target another pull request’s branch, as long as the stack ends at the queue’s branch.
    • A repository ruleset can block it.
    • It’s still in another of the repository’s queues. Look up the pull request, read queueEntry.mergeQueueId, and dequeue it there first.
  • A closed pull request is accepted but stays notReady.
To wait for a pull request to merge, poll GET /v2/pull-requests/{id} every 30 to 60 seconds until queueEntry.state is merged, failed, or cancelled, or subscribe to Merge Queue webhooks. The API itself has no webhooks.
  • Read queueEntry.state, not the top-level state, which is the pull request’s state on GitHub.
  • queueEntry.state can go back to notReady before the entry ends. A push to the pull request sends a queued entry back to notReady to wait on the new commit, and a resubmitted pull request reuses its entry and starts again at notReady. Keep polling.
  • Stop if queueEntry disappears or its mergeQueueId changes, and report the outcome as unknown. This happens when a failed or cancelled pull request is submitted to another queue, or its queue is deleted.
  • Send the last response’s ETag in If-None-Match. If nothing changed, you get 304 with no body.
  • Set a deadline of your own. A pull request can stay notReady for a long time; the queue cancels it only if its notReadyTimeoutMinutes setting is set.
  • Don’t stop when ci.pollAfterSeconds is null. It describes CI on the pull request’s own head commit, not its place in the queue.
To find out why a pull request failed, read the latest transition in its queue-entry history (direction=newest). The failure reason reference explains each reason. When a required check failed, failed.failureCausingChecks names the checks, and verificationRunId leads to the verification run and its checks.

Pagination

Every list endpoint returns the same envelope:
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, from 1 to 100; the default is 25. includeTotal=true adds a total count at the cost of an extra query. List order is not queue order. A queue’s pull request list is ordered by when each pull request was first submitted to the queue, newest first, and a resubmission doesn’t move it. For the order the queue will test pull requests in, use GET /v2/merge-queues/{id}/order. Its lists carry only each pull request’s id, number, title, sha, and url. For state or priority, match by id against GET /v2/merge-queues/{id}/pull-requests?state=pending,testing,testsPassed,pendingFailure.

Errors

Every error the API itself returns has this shape:
Switch on code, which is stable. message is for people and may change at any time. Some responses don’t have this shape. Trunk’s edge answers 401, 403 for some credential problems, 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. See Authentication errors.
  • 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.
  • 422 means the request can’t succeed as sent. Retrying it unchanged gets the same answer until something about the pull request or repository changes.
Every response from the API carries an X-Request-Id header; edge responses may not. Include it when you contact support.

Rate limits

Most requests share one limit of 100 per second per organization, across every API key, user, and CLI session in it. Some requests have limits of their own and don’t count toward it, including:
  • GET requests on paths that start with /v2/pull-requests, such as GET /v2/pull-requests/{id} and its history: 5 per second per API key or user. GET /v2/merge-queues/{id}/pull-requests uses the organization’s limit.
  • Metrics (GET /v2/merge-queues/metrics and GET /v2/merge-queues/{id}/metrics): 5 per second per organization.
  • Impacted-target uploads from a GitHub Actions login: 10 per second per repository, with a separate allowance for fork pull requests.
A request over a limit gets 429.

Retrying

  • Reads are safe to retry on 429, 500, 502, 503, 504, and network errors. Back off exponentially between attempts.
  • Writes don’t honor Idempotency-Key yet. A write that times out or returns 500, 502, 503, or 504 has an unknown outcome, so read the resource back before retrying. A blind retry can, for example, restart a pull request’s tests twice. Some writes, such as PATCH …/config and PUT /v2/impacted-targets, set a value and are safe to repeat.

Compatibility

Within v2, Trunk may add endpoints, optional request parameters, optional response fields, and new enum values, including error codes and queue-entry states. Removals and changes in meaning require a new major version. Tolerate values and fields you don’t recognize. Most enum values are camelCase. These are uppercase: error codes, a transition’s reason and pendingFailure.eventualFailureReason, a check’s providerStatus and providerConclusion, and failureCausingChecks[].conclusion.

Endpoints

Each endpoint page is also available as Markdown, including its OpenAPI definition, by adding .md to its URL.

Merge queues

List merge queues

Create a merge queue

Get a merge queue

Update a merge queue's configuration

Switch a merge queue's mode

Pause a merge queue

Resume a merge queue

Drain a merge queue

Delete a merge queue

Queue order

The order shows what the queue tests next. The graph shows why pull requests depend on each other, not when they run.

Get a merge queue's dispatch order

Get a merge queue's dependency graph

Pull requests

Submit a pull request to a merge queue

List a merge queue's pull requests

Resolve pull requests by repository coordinates

Get a pull request

Reprioritize a queued pull request

Update a queued pull request's entry

Restart a queued pull request's tests

Dequeue a pull request

List a pull request's queue-entry state history

List queue-entry state history for several pull requests

Estimate when a queued pull request will merge

Testing

List a merge queue's verification runs

List a pull request's verification runs

Get a verification run

List a merge queue's completed testing

Impacted targets

A queue in graph mode (Parallel in the app) waits for CI to upload the build targets each pull request impacts before testing it. See Parallel queues.

Upload a pull request's impacted targets

Upload impacted targets from GitHub Actions

Metrics

These return Prometheus text format with v1’s metric names and labels. See Prometheus metrics for the metrics, example queries, and moving a v1 scrape job.

Get Prometheus metrics for every merge queue

Get Prometheus metrics for a merge queue

Repositories

Resolve a repository by its provider URL

Get a repository