HTTP API

49 operations under /v1, 27 of them also exposed as MCP tools. Generated from the same registry the router matches against.

Calling the API

JSON in, JSON out, one bearer credential. Unknown fields are rejected rather than ignored — a typo in a body is a 400, not a silent default.

curl https://app.trunk.dev/v1/me \
  -H "Authorization: Bearer $AGENT_KEY"

Live events are the one thing not in the registry: GET /v1/stream is server-sent events, with ?since=<event-id> to replay from a point. This UI uses it so it never polls.

Open the authenticated API console

Identity

Who the credential belongs to, which services and paths it may touch, what it has left to spend today, and how its changes have fared. An agent should read this before it proposes anything.

GET/v1/me
trunk_whoami

Who am I, what may I touch, and what is my record

Call this first. Returns your scopes (which services and paths you may change), your remaining budget for today, and your measured track record per service — the same numbers policy uses to decide whether your next change lands unattended.

returns ActorProfileme.get

Changes

The whole lifecycle: propose, verify, decide, land, revert. A change is the only thing that reaches trunk — there is no branch to push and no pull request to open.

POST/v1/changes
trunk_propose_change

Propose a change — the only way code reaches trunk

Submits a complete change object: your stated intent, the spec you worked to, the diff, the results you observed, and your session trace. Trunk verifies the diff itself, scores risk against your track record in this service, and applies repo policy. Configured required external checks are created and dispatched automatically for the exact immutable candidate revision; callers do not need to start Trunk-managed CI manually. If policy says auto_land and every required external check is green for the current revision, an ordinary Change lands. A Draft-controlled revision remains open until that exact revision is marked Ready. Human-routed changes still require a decision, and neither approval nor Ready bypasses required gates.

Body
repo_idrequiredstring
Repository id or name.
titlerequiredstring
Imperative one-liner, as it will read on trunk.
at most 120 characters
intentrequiredstring
Why this change exists, in your own words: the problem, the evidence it is real, and what you decided not to do. This is what a human reads first.
at least 20 characters
diffrequiredstring
Unified diff (git format). Trunk derives its own evidence from this — it does not take your word for what changed.
at least 10 characters
servicestring
Owning service. Inferred from the changed paths when omitted.
branchstring
The branch this change lives on. Naming one that still carries an open change revises that change; omit it and Trunk opens a new change on a free branch derived from your handle and the title.
at most 200 characters
specobject
goalstring
non_goalsstring[]
acceptance_criteriastring[]
constraintsstring[]
base_commitstring
Trunk commit you worked from. Defaults to current head.
reportedobject
Results you observed. Recorded as self-reported and weighted as such.
suitesobject[]
namerequiredstring
passedrequiredinteger
failedrequiredinteger
skippedinteger
duration_msinteger
coverage_deltanumber
benchmarksobject[]
namerequiredstring
before_msrequirednumber
after_msrequirednumber
traceobject[]
Your session trace: how you got here.
kindrequiredstring
What the agent was doing at this step.
one of read · search · reason · edit · test · bench · tool · note
summaryrequiredstring
One line, written for a human skimming the trace.
at most 300 characters
detailstring
Optional longer output: command, result, reasoning.
tsstring
ISO timestamp. Defaults to now.
tokensinteger
Tokens consumed by this step.
usdnumber
Cost of this step in USD.
cost_usdnumber
What this change cost to produce. Counts against your budget.
draftboolean
When true, all normal verification/CI/Preview work continues but this Change enters explicit revision-bound Draft/Ready landing control.
returns Changechanges.propose
GET/v1/changes
trunk_list_changes

List changes

Query
repo_idstring
servicestring
branchstring
Only changes on this branch.
authorstring
Actor handle or id.
statusstring
Comma-separated: verifying, awaiting_decision, approved, landed, reverted, blocked, rejected.
routestring
one of auto_land · human_review · owner_review · block
limitinteger
1–200
defaults to 25
returns ChangeListchanges.list
GET/v1/changes/:change_id
trunk_get_change

Get one change, with whichever parts you need

Path
change_idrequiredstring
The change id to act on.
Query
includestring
Comma-separated: diff, evidence, trace, decisions, track.
defaults to "evidence,trace,decisions,track"
returns Changechanges.get
POST/v1/changes/:change_id/verify
trunk_verify_change

Re-run verification and re-route

Rebuilds the evidence bundle from scratch, re-scores risk against the current track record, and re-applies policy. Any approval bound to an older evidence digest is invalidated. After verification materializes the current immutable candidate, configured required external checks are dispatched automatically. Re-running verification safely reuses/reconciles the same durable first-attempt CI identity.

Path
change_idrequiredstring
The change id to act on.
returns VerifyResultchanges.verify
POST/v1/changes/:change_id/decisions
human only

Record a human decision or terminate a pre-evidence Change

Human-only. request_key is the caller-stable idempotency boundary: retry the exact same request with the same key after any lost or ambiguous response; reusing a key with different input fails closed. With verification evidence present, a decision is bound to that evidence. Before verification evidence exists, only reject is accepted and no Decision row is recorded. The response distinguishes the durable Decision outcome from the follow-on landing outcome; approval does not bypass required external checks.

Path
change_idrequiredstring
The change id to act on.
Body
request_keyrequiredstring
Caller-stable idempotency token for this logical decision request. Retry with the same key after any lost or ambiguous response.
1–128 characters
verdictrequiredstring
one of approve · reject · request_changes
evidence_digeststring
The digest you read for an evidence-bound decision. With evidence present, omitting it accepts whatever evidence is current; before evidence exists, only reject is allowed and no Decision row is recorded.
at least 1 characters
notestring
What convinced you, or what is missing.
returns ChangeDecisionResultchanges.decide
POST/v1/changes/:change_id/ready
trunk_mark_change_ready

Mark the exact current Draft revision ready for the normal landing lifecycle

Only the Change author may open landing readiness for the exact numerical Change revision supplied by the caller. It creates no evidence and bypasses no policy, approval, check, Preview, freshness, source or publication fence. A stale revision is rejected.

Path
change_idrequiredstring
The change id to act on.
Body
revisionrequiredinteger
Exact current numerical Change revision being released from Draft.
1 or more
returns Changechanges.mark_ready
POST/v1/changes/:change_id/revert
trunk_revert_change

Revert a landed change

Reverts on trunk and attributes the outcome to the change's author. This is what moves an agent's track record, which is what changes how its next change routes.

Path
change_idrequiredstring
The change id to act on.
Body
reasonrequiredstring
What went wrong. Shown on the author's record.
at least 5 characters
detected_bystring
Alert, on-call, customer report, canary…
returns RevertResultchanges.revert
POST/v1/changes/:change_id/trace
trunk_append_trace

Append steps to a change session trace

Path
change_idrequiredstring
The change id to act on.
Body
stepsrequiredobject[]
kindrequiredstring
What the agent was doing at this step.
one of read · search · reason · edit · test · bench · tool · note
summaryrequiredstring
One line, written for a human skimming the trace.
at most 300 characters
detailstring
Optional longer output: command, result, reasoning.
tsstring
ISO timestamp. Defaults to now.
tokensinteger
Tokens consumed by this step.
usdnumber
Cost of this step in USD.
returns Tracechanges.trace

The decision queue

What policy could not resolve on evidence alone. If this is empty, everything that landed today landed unattended.

Repositories

A repository owns services, the routing policy and the trunk log. Services are matched from changed paths, which is how a change finds its owner.

POST/v1/repos/:repo_id/git-credentials
human only

Issue a short-lived human Git HTTPS credential

Issues a short-lived credential for the repository’s Trunk-owned HTTPS Git remote. Read credentials permit clone/fetch. Write credentials additionally return the authenticated actor’s allowed Human Work ref prefix; Trunk landing remains the only normal canonical publication path.

Path
repo_idrequiredstring
The repo id to act on.
Body
accessrequiredstring
Requested Git authority.
one of read · write
returns GitCredentialrepos.gitCredential
POST/v1/repos/:repo_id/import
trunk_import_repository

Start or resume an initial repository import

Requires repository-admin authority. Reserves one durable import operation using bounded commit/source/digest/size metadata, or reconciles the same operation after an interrupted raw-pack upload. Bulk Git-pack bytes are uploaded separately to the returned Trunk-owned binary transfer URL and never travel through JSON or MCP.

Path
repo_idrequiredstring
The repo id to act on.
Body
commitrequiredstring
Pinned Git SHA-1 commit to import.
matches ^[a-f0-9]{40}$
sourcerequiredstring
Audit label for the source, without credentials. Never fetched by the server.
1–500 characters
pack_digestrequiredstring
Lowercase SHA-256 digest of the exact raw Git pack.
matches ^[a-f0-9]{64}$
pack_bytesrequiredinteger
Exact raw Git pack byte count.
32 or more
returns RepositoryImportrepos.import
GET/v1/repos/:repo_id/import
trunk_get_repository_import

Inspect the durable repository import operation

Requires repository-admin authority. Returns bounded metadata and the durable import state. This operation never carries Git-pack bytes or performs provider mutation.

Path
repo_idrequiredstring
The repo id to act on.
returns RepositoryImportrepos.import.get
POST/v1/repos
human only

Create a repository

Creates a repository, its services, and version 1 of its routing policy. Services are how Trunk assigns a change to an owner and a criticality, and they are what track records are measured against — a repository with no services still works, but every change lands as "unassigned" at medium criticality. Human-only: registering repositories is not an agent operation. The creating human becomes repository administrator and check administrator in the durable preparation transaction before provider provisioning begins. CI is disabled by default. A repository admin can enable Trunk-managed CI from Integrations.

Body
namerequiredstring
A single lowercase slug. Repositories are not namespaced.
at most 40 characters
trunk_branchstring
The branch changes land on. Defaults to "main".
at most 60 characters
servicesobject[]
Path globs decide which service a change belongs to.
namerequiredstring
at most 40 characters
criticalitystring
one of low · medium · high · critical
owner_teamstring
at most 60 characters
path_globsstring[]
e.g. services/payments/**
rulesobject[]
Routing policy. Defaults to the built-in rules.
returns RepoDetailrepos.create

Policy

The rules that decide what lands unattended. Policy is versioned, and a candidate can be replayed over historical changes before it is adopted.

GET/v1/repos/:repo_id/policy
trunk_get_policy

Read the active routing policy

Path
repo_idrequiredstring
The repo id to act on.
returns PolicyDocumentpolicy.get
PUT/v1/repos/:repo_id/policy
human only

Replace the routing policy (creates a new version)

Path
repo_idrequiredstring
The repo id to act on.
Body
rulesrequiredobject[]
idstring
whenobject
thenstring
one of auto_land · human_review · owner_review · block
reasonstring
returns Policypolicy.set
POST/v1/repos/:repo_id/policy/simulate
trunk_simulate_policy

Replay a candidate policy over historical changes

Answers the only question that matters before changing policy: how many of the last N changes would this have auto-landed, and which ones would it have stopped?

Path
repo_idrequiredstring
The repo id to act on.
Body
rulesrequiredobject[]
limitinteger
200 or less
defaults to 50
returns PolicySimulationpolicy.simulate

Source

The code itself, read from whichever Git provider holds it. Trunk lands on one branch and creates no others, so the branch list is the provider’s, not Trunk’s — and a read is only served for a branch that is actually there.

POST/v1/repos/:repo_id/files/read
trunk_read_sources

Read several files from one exact branch snapshot

Reads up to 12 repository files in one call. The named branch is resolved once and every file is read at that immutable head commit. Missing paths are reported separately. Aggregate returned text is capped at 2097152 bytes.

Path
repo_idrequiredstring
The repo id to act on.
Body
pathsrequiredstring[]
Repository-relative file paths. At most 12.
refstring
Branch name. Defaults to the trunk branch.
returns SourceFilesContentsource.files
POST/v1/repos/:repo_id/patch/preflight
trunk_preflight_patch

Preflight a textual patch against the exact recorded Trunk head

Read-only exact applicability check for an existing unified Git text diff. Trunk captures its current recorded commit SHA once, reads affected paths at that immutable commit, and verifies supported add/modify/delete hunks without fuzzy relocation. The result is convenience evidence for a later trunk_propose_change call using the returned base_commit; it does not reserve, authorize, materialize, or mutate anything. Diff input is capped at 524288 bytes and source reads reuse the bounded multi-read limits. Rename/copy, binary, mode-only and no-trailing-newline forms fail closed.

Path
repo_idrequiredstring
The repo id to act on.
Body
diffrequiredstring
1–524288 characters
returns PatchPreflightResultsource.patch_preflight
GET/v1/repos/:repo_id/branches
trunk_list_branches

The branches this repository holds

Trunk lands on one branch and creates no others, so this is whatever the underlying Git provider actually has. The trunk branch is flagged and listed first. Reads are only served for a branch that appears here.

Path
repo_idrequiredstring
The repo id to act on.
returns SourceBranchessource.branches
GET/v1/repos/:repo_id/tree
trunk_list_source

One directory of the source at one branch

Directories first, then files, each alphabetically. Omit `path` for the repository root, and `ref` for the trunk branch. A `ref` naming no branch is a 404 rather than a silent fall back to trunk.

Path
repo_idrequiredstring
The repo id to act on.
Query
refstring
Branch name. Defaults to the trunk branch.
pathstring
Directory to list. Defaults to the repository root.
at most 1024 characters
returns SourceTreesource.tree
GET/v1/repos/:repo_id/file
trunk_read_source

One file of the source at one branch

Text arrives as `text`; a blob that is not decodable text comes back with `text: null` and `binary: true`. Large files are truncated to whole lines with `truncated: true`, and `bytes` is always the size of the whole blob.

Path
repo_idrequiredstring
The repo id to act on.
Query
pathrequiredstring
File to read, from the repository root.
at most 1024 characters
refstring
Branch name. Defaults to the trunk branch.
returns SourceFileContentsource.file

The trunk log

What landed, in order, with the change and the evidence behind each commit.

GET/v1/repos/:repo_id/trunk
trunk_log

The trunk log: what landed, in order

Path
repo_idrequiredstring
The repo id to act on.
Query
limitinteger
500 or less
defaults to 50
returns TrunkLogtrunk.log

Agents

The fleet, its scopes and its measured record. Registering an agent and granting it scope are human acts and stay human-only.

POST/v1/agents
human only

Register an agent identity

Human-only. Returns the API key once; it is not retrievable afterwards.

Body
handlerequiredstring
matches ^[a-z][a-z0-9-]{1,30}$
displayrequiredstring
modelstring
Which model drives this agent.
operatorstring
The team accountable for it.
returns NewAgentagents.create
DELETE/v1/agents/:agent_id
human only

Delete an agent that never did anything

Human-only. Removes the identity along with its scopes, budget and API keys. Refused with a 409 once the agent has a record — a proposed change, a revert, an attested check run. That history is signed by this actor and policy reads it, so it outlives any wish to tidy the fleet up; revoke the agent's keys instead, which stops it working and leaves what it did readable.

Path
agent_idrequiredstring
The agent id to act on.
returns DeletedAgentagents.delete
POST/v1/agents/:agent_id/scopes
human only

Grant an agent a scope

Path
agent_idrequiredstring
The agent id to act on.
Body
repo_idrequiredstring
servicestring
defaults to "*"
path_globsstring[]
Glob patterns the agent may modify, e.g. services/payments/**
opsstring[]
returns Scopeagents.grant
PUT/v1/agents/:agent_id/budget
human only

Set an agent budget

Path
agent_idrequiredstring
The agent id to act on.
Body
changes_per_dayinteger
0 or more
usd_per_daynumber
0 or more
max_diff_linesinteger
1 or more
max_filesinteger
1 or more
returns Budgetagents.budget

API keys

An agent key is shown once at issue and stored only as a digest. Human-only: an agent cannot widen its own credentials.

GET/v1/agents/:agent_id/keys
human only

List an agent's active API keys

Names and previews only. A key itself is readable exactly once, when it is issued.

Path
agent_idrequiredstring
The agent id to act on.
returns ApiKeyListkeys.list
POST/v1/agents/:agent_id/keys
human only

Issue a new API key

Returns the key once. Only its digest is stored, so it cannot be recovered afterwards — reissue instead. An agent may hold several keys at once, which is how you rotate without downtime: issue the new one, move the agent across, then revoke the old one.

Path
agent_idrequiredstring
The agent id to act on.
Body
namestring
What this key is for, e.g. "ci" or "laptop".
at most 60 characters
returns IssuedApiKeykeys.create
DELETE/v1/agents/:agent_id/keys/:key_id
human only

Revoke an API key

The key stops authenticating immediately. The record of it is kept, so a key that was used and then taken away stays visible.

Path
agent_idrequiredstring
The agent id to act on.
key_idrequiredstring
The key id to act on.
returns RevokedApiKeykeys.revoke

Events

The platform event stream — every proposal, routing decision, landing and revert.

GET/v1/events
trunk_recent_events

Recent platform events

Query
limitinteger
500 or less
defaults to 50
sinceinteger
defaults to 0
change_idstring
returns EventListevents.list

Statistics

Throughput and autonomy for a repository: what was proposed, what landed, and how much of it needed a person.

Accounts and sessions

Human sign-up, sign-in and sign-out. Not MCP tools: registering people is not an agent operation.

POST/v1/auth/signup
no credentialhuman only

Create an account

Creates a human identity and opens a session. Agents cannot call this: registering people is not an agent operation.

Body
emailrequiredstring
at most 254 characters
passwordrequiredstring
At least 10 characters.
10–512 characters
namerequiredstring
Display name, shown on decisions you record.
at most 120 characters
handlestring
Optional; derived from your name otherwise.
matches ^[a-z][a-z0-9-]{1,30}$
returns Sessionauth.signup
POST/v1/auth/signout
human only

End the current session

Body
everywhereboolean
End every session for this account.
returns SignedOutauth.signout

externalCi

POST/v1/changes/:changeId/ci-dispatches
human only

Dispatch or retry an exact required check in Trunk-managed CI

Manual/admin handoff for a configured Trunk-managed required check after Trunk has materialized and verified an immutable candidate. Normal first-attempt CI is orchestrated automatically; this surface remains human-attester-only for explicit recovery/bootstrap and terminal retry. The requested provider/check is revalidated as a required repository check under the same repository lock that creates the queued CheckRun and durable dispatch intent. The stable managed-CI provider identity is trunk-ci; the backing execution provider is intentionally not part of this public contract. The caller supplies the exact candidate SHA it observed; Trunk binds the request to the current numbered Change revision and uses the same rolling-safe first-attempt identity selection as automatic orchestration, so manual/automatic races converge while distinct numbered revisions can never collapse merely because they resolve to the same candidate SHA. To rerun a terminal non-success provider-owned check on the same revision, retryOf names the exact prior durable dispatch; Core derives exactly one child request identity from that parent, so replay is idempotent and concurrent retries cannot fan out.

Path
changeIdrequiredstring
The changeId to act on.
Body
providerrequiredstring
Stable Trunk-managed CI provider identity. Use trunk-ci.
one of trunk-ci
checkNamerequiredstring
1–120 characters
revisionrequiredstring
40–64 characters · matches ^[0-9a-fA-F]+$
retryOfstring
1–200 characters
returns ExternalCiDispatchexternalCi.dispatch

deployments

POST/v1/repos/:repoId/deployments
human only

Create or replay an exact-revision deployment and dispatch supported Preview providers

Server-only deployment handoff for an explicitly authorized deployer. Durable deployment intent is committed before provider handling. New Vercel Preview creates resolve the repository's durable Vercel installation/project connection, capture the exact immutable Trunk candidate source proof, and stay durably NOT_STARTED while read-only preflight positively proves that project already has a Production deployment. Only then may Trunk publish STARTED and transport the exact candidate directly to Vercel without a GitHub bridge or Production target. Existing STARTED attempts continue to reconcile against their pinned provider target, including the recovery-only legacy target format used before repository-scoped integrations, and require authoritative provider read-back. Historical unsupported SHA-256 Preview attempts that are still positively pre-mutation are retired to cancelled on replay rather than remaining queued forever; STARTED and terminal historical attempts retain their existing recovery/replay semantics. Terminal idempotent replays return durable state without requiring historical provider credentials. Production remains intent-only and therefore cannot invoke Vercel here.

Path
repoIdrequiredstring
The repoId to act on.
Body
revisionrequiredstring
matches ^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$
environmentrequiredstring
one of preview · production
providerrequiredstring
matches ^[a-z0-9][a-z0-9._-]{0,63}$
requestKeyrequiredstring
1–200 characters
changeIdstring
1–200 characters
returns Deploymentdeployments.start
PUT/v1/deployments/:deploymentId
human only

Report provider-observed state for one deployment attempt

Server-only provider-neutral result handoff. Any currently authorized deployer for the repository may reconcile the attempt, which permits credential rotation and failover. The deployment domain still enforces monotonic state, immutable terminal outcomes, immutable provider identity/URL and idempotent replay.

Path
deploymentIdrequiredstring
The deploymentId to act on.
Body
statusrequiredstring
one of queued · in_progress · ready · failed · cancelled
externalIdstring
at most 200 characters
urlstring
at most 2000 characters
detailsUrlstring
at most 2000 characters
returns Deploymentdeployments.update

checks

GET/v1/repos/:repoId/check-requirements
human only

Read the required external checks for a repository

Returns stable public check identities. Trunk-managed CI is identified as trunk-ci; customer-selected external providers retain their provider identity.

Path
repoIdrequiredstring
The repoId to act on.
returns CheckRequirementschecks.requirements.get
PUT/v1/repos/:repoId/check-requirements
human only

Replace the required external checks for a repository

Repository check-admin policy. Providers report check state; they never decide whether their own check is required for landing. Public signup alone grants no check-admin authority. Trunk-managed CI uses stable trunk-ci/<workflowName> identities; provider-specific managed execution remains internal. Policy storage commits before reconciliation. New managed requirements are activated for already-verified candidate revisions, while only already-approved changes may resume landing. If post-commit managed-CI activation is unavailable, the request reports a provider-neutral retryable error and the saved policy remains authoritative.

Path
repoIdrequiredstring
The repoId to act on.
Body
requiredrequiredobject[]
namerequiredstring
1–120 characters
providerrequiredstring
matches ^[a-z0-9][a-z0-9._-]{0,63}$
returns CheckRequirementschecks.requirements.set
POST/v1/changes/:changeId/checks
human only

Create a human-attested check run for the current change revision

Bootstrap surface for human-attested or non-integrated check providers. The Trunk-managed CI identity is reserved for its authenticated handoff/result path, so a human cannot manufacture a later managed attempt that supersedes provider-owned evidence. Genuine customer-selected or non-managed providers retain their own provider identity. Other checks require repository check-attester authority and reject stale revisions.

Path
changeIdrequiredstring
The changeId to act on.
Body
namerequiredstring
1–120 characters
providerrequiredstring
matches ^[a-z0-9][a-z0-9._-]{0,63}$
revisionrequiredstring
1–128 characters
externalIdstring
Provider run id for idempotent creation/replay.
at most 200 characters
detailsUrlstring
at most 2000 characters
returns CheckMutationchecks.create
PUT/v1/checks/:checkId
human only

Advance a human-attested check run and resume landing when the gate turns green

Check state is monotonic. A completed conclusion is immutable; replaying the same update is idempotent and emits no duplicate event. Only the explicitly repository-authorized actor that created the attestation may update it, and CheckRuns bound to external-CI dispatches are provider-owned and rejected by this human surface.

Path
checkIdrequiredstring
The checkId to act on.
Body
statusrequiredstring
one of queued · in_progress · completed
conclusionstring
one of success · failure · cancelled · timed_out · skipped
detailsUrlstring
at most 2000 characters
returns CheckMutationchecks.update