Skip to main content
This page shows how to drive a merge queue from your own scripts and CI with the Trunk API. For authentication, errors, pagination, and rate limits, see the API reference’s Getting started pages. Each endpoint has its own page in the API reference.

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

Send an API key as Authorization: Bearer <key>. A key with no permissions can read merge queues but can’t change them. Writes need the Merge Queue permissions listed on each step below. A GitHub Actions job that uploads impacted targets can authenticate without a key.

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.

Queue order

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.

Retrying

Writes don’t honor Idempotency-Key yet. A write that times out or returns a 5xx has an unknown outcome, so read the resource back before you retry it. 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. See Status codes and errors.