Docs
Trunk's business operations are declared in one registry that drives the HTTP API, generated references, MCP tools and TypeScript client. Protocol surfaces such as delegated MCP OAuth intentionally live outside the /v1 operation registry and are documented alongside those generated surfaces.
One registry, four surfaces
Each business operation is declared once, in src/api/operations.ts, with its schema, its handler and the name it takes as an MCP tool. The HTTP route table, the OpenAPI document, the tool definitions and the TypeScript client are all derived from that declaration. Protocol endpoints such as OAuth discovery, authorization and token exchange stay outside this business-operation registry on purpose.
| Surface | What it is | Where |
|---|---|---|
| HTTP API | 49 operations under /v1, JSON in and out. | openapi.json |
| MCP server | 27 of those operations as tools an agent can call directly. The other 22 are human-only. | mcp.json |
| TypeScript client | Generated from the same schemas, with the response types the server is compile-checked to return. | trunk-client.ts |
| This UI | A client of the API like any other. It has no privileged path into the system. | you are here |
The API reference is grouped the way the system is: identity, changes, the decision queue, repositories, policy, source, the trunk log, agents, api keys, events, statistics, accounts and sessions, externalci, deployments, checks.
Credentials
Most /v1 calls use human session tokens or agent API keys; sign-up and sign-in are the public business-operation exceptions. Remote MCP clients may instead use a delegated OAuth bearer bound to /mcp, which is not generic /v1 authority.
Authorization: Bearer <agent-key-or-session-token>- Agent keys are issued when an agent is registered and shown exactly once. Trunk stores a digest, so a lost key is replaced, never recovered.
- Session tokens come from
POST /v1/auth/signinand expire. The browser holds one in a cookie, which is why the dashboard API console works without you pasting anything. - Delegated MCP OAuth lets a compatible remote client authorize through Trunk sign-in and consent instead of receiving an agent key. The resulting bearer is resource-bound to
/mcpand is deliberately separate from generic/v1authority. See Remote OAuth clients. - Agent scopes constrain the operations that explicitly enforce repository, service, path and action authority. They do not replace the tenant/repository boundary used by ordinary repository and source reads. Registering agents, granting scope, setting budgets and issuing keys remain human-only operations.
- Most business operations require authenticated authority.
POST /v1/auth/signupandPOST /v1/auth/signinare the public/v1exceptions. OAuth discovery, registration, authorization, token and revocation are separate protocol routes with their own OAuth validation; OAuth access tokens are accepted only by the protected/mcpresource.
Connect an agent
Over HTTP against this deployment, or over stdio against a checkout you run yourself. Both transports feed the same dispatcher, so the tools behave identically.
# against this deployment with an existing agent key claude mcp add --transport http trunk https://app.trunk.dev/mcp \ --header "Authorization: Bearer $AGENT_KEY" # against a local checkout, over stdio # run from the repository root; AGENT_KEY and DATABASE_URL are already set claude mcp add \ --env TRUNK_API_KEY="$AGENT_KEY" \ --env DATABASE_URL="$DATABASE_URL" \ --transport stdio trunk \ -- npm --prefix "$PWD" run mcp
Run the local command from the repository root. Your shell expands $PWD while registering the server, so Claude stores the exact checkout path and later MCP starts do not depend on Claude's working directory. The root npm entrypoint delegates to the dashboard MCP server. DATABASE_URL selects the Trunk Postgres database. The stdio process can start without TRUNK_API_KEY, but tool calls require a valid agent key.
OAuth-capable remote MCP clients can instead connect to https://app.trunk.dev/mcp through discovery and Trunk's sign-in/consent flow without a pre-created agent key. The MCP OAuth guide explains the delegated authority boundaries.
Prefer plain HTTP? The generated client takes an agent key or session token — new TrunkClient({ baseUrl, apiKey }) — and every operation is a method on it. Delegated OAuth bearers are intentionally not generic client credentials for /v1.
How a change lands
There is no pull request to open and no queue to sit in. A Change is proposed complete onto a branch of its own; policy, author readiness and required evidence then independently determine when that exact revision may land.
- Ask what you may do.
trunk_whoamireturns your scopes, your remaining budget and your measured record per service — the same numbers policy is about to use. - Propose the whole change. Intent, the spec you worked to, the diff, the results you observed and your session trace, in one call to
POST /v1/changes. Name abranchor let Trunk derive one; either way the Change lives there until it lands. A proposal may intentionally hold its exact current revision as Draft; non-Draft work keeps the ordinary automatic-landing behavior. - Trunk derives its own evidence. It parses the diff and runs its verifiers over it rather than taking your word for what changed. Anything you reported is recorded as self-reported and weighted as such.
- Policy routes it. Risk is scored against your track record in that service; the repo's rules decide between landing unattended, a human review, the service owner, or a block.
- Draft does not stop evidence. Verification, required checks, CI and Preview may continue while the exact current revision is Draft. The author-controlled hold only prevents that Draft revision from receiving new landing authority.
- Ready releases only the author's hold. Only the exact author may mark the exact current Draft revision Ready. Ready does not approve the Change or bypass policy, checks, Preview, freshness, verification or repository publication; it simply lets that revision re-enter the ordinary landing lifecycle.
- Revise on the same branch. Propose again with the same
branchand you revise the one Change — same id, same thread of evidence and decisions — instead of opening a second one beside it. Each revision is its own immutable candidate for CI to fetch, and a new content revision does not inherit Ready authority from the revision before it. - A decision is bound to the evidence. Approval cites an evidence digest; if the diff moves, the approval goes stale rather than silently covering something nobody read.
When a call fails
Errors are JSON, with a stable code and — for a schema failure — the exact paths that did not validate.
| Status | Code | Means |
|---|---|---|
| 400 | invalid_request | The body or query did not match the operation schema. `details` lists each path and message. |
| 400 | invalid_json | The body was not JSON. |
| 401 | unauthenticated | No credential, or one Trunk could not resolve. |
| 403 | forbidden | Out of scope for this actor — a path, a service, or an operation reserved for humans. |
| 403 | suspended | The actor exists but is suspended. |
| 404 | not_found | No such operation, or no such record. |
| 409 | conflict | Stale evidence, a missing approval, or a change already blocked or landed. |