Reference

Version 5 · Updated

API

Version 1: authentication, errors, paging, safe retries, rate limits, the SDK and every endpoint.

Everything the web app, the CLI and MCP do goes through Branchscope's HTTP API, version 1. Every path starts with /v1. The API is at http://<host>:3001 locally; any host names its address with GET <web>/.well-known/branchscope (api).

  • The OpenAPI document: GET /v1/openapi.json describes every endpoint: its parameters, body, response and errors. Generate a client from it in any language.
  • The reference: every endpoint with an example, in the API reference.
  • The TypeScript SDK: @branchscope/sdk (below).

Authentication#

Send Authorization: Bearer <token>:

  • a personal token (bsc_pat_…);
  • an OAuth app's token (bsc_oat_… for a person, bsc_ait_… for an installation), limited to its scopes;
  • or a Keycloak access token.

Without a credential you see only public repositories. A few things only a signed-in person in the web app can do, never a token, because they grant access: creating tokens, adding SSH keys, creating organizations and managing their members.

Versions#

/v1 is stable: fields and endpoints are added to it, never removed or changed. A change that would break callers comes as /v2.

Errors#

Every error is JSON with an HTTP status and a stable code:

{ "error": { "code": "guard_denied", "message": "main is a protected branch: agents can't update it", "details": { "message": "…" } } }
CodeStatusMeans
invalid_request400a parameter or the body is wrong; details.issues lists each field
unauthorized401sign in, or the credential isn't valid
forbidden403not allowed
guard_denied403the push guard refused a merge; details.message is its reason
not_found404doesn't exist, or you can't see it (private repositories you can't read are not found)
conflict409the state doesn't allow it, such as merging a closed change; a merge's conflicting files are in details.conflicts
not_ready409not yet: a graph still building, a review still running
idempotency_in_progress409a request with the same Idempotency-Key is still running
gone410stored data is no longer there, such as a deleted graph
idempotency_mismatch422the Idempotency-Key was used for a different request
rate_limited429over the rate limit; wait for Retry-After
internal500Branchscope's fault
upstream_error502the git service couldn't be reached
guard_unavailable503the push guard couldn't be reached, so the merge was refused

Use the code, not the message: messages are for people and can change.

Lists#

Every list is paged. It returns { "data": [...], "nextCursor": "…" } and takes ?limit= (1-100, default 50) and ?cursor=. Pass nextCursor back as cursor for the next page; it's null on the last one. Cursors carry on where the last page stopped even as items are added, so nothing is shown twice.

Filters
changesstatus (open, merged, closed), author, agent
activity and agent activityrepo, actor (a username), agent, since and until (ISO 8601 times)

Retrying safely#

Creates and merges accept an Idempotency-Key header: any unique string, up to 200 characters. The first response to a key is kept for 24 hours, for your credential. Sending the same request with the same key gets that response back (with Idempotent-Replayed: true) instead of acting twice. So an agent that lost a connection can retry a merge without merging twice. A failed request frees its key, so it can be retried.

Rate limits#

WhoLimit
each token1,000 requests an hour, and 60 a minute
anonymous callers60 requests an hour, per IP address

Each token counts on its own, so an agent's token can't use up its person's. The web app makes several requests per page, so it has a larger budget for its users and visitors. Site operators can change the limits.

Every response says where you are: RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the window starts again). Over the limit is 429 rate_limited, with Retry-After in seconds.

The TypeScript SDK#

@branchscope/sdk is a typed client: one method per endpoint, typed from the same schemas as the API.

import { Branchscope, BranchscopeError } from "@branchscope/sdk";

const branchscope = new Branchscope({ baseUrl: "https://api.example.com", token: process.env.BRANCHSCOPE_TOKEN });

// Awaiting a list gives one page; for await walks every item across pages.
for await (const change of branchscope.changes.list({ owner: "acme", name: "web", status: "open" })) {
  console.log(change.number, change.title);
}

try {
  await branchscope.changes.merge({ owner: "acme", name: "web", number: 12 });
} catch (error) {
  if (error instanceof BranchscopeError && error.code === "guard_denied") console.log(error.message);
}
  • Errors are BranchscopeError, with status, code, message and details.
  • Retries: after a 429 it waits for Retry-After. It retries server errors and network failures only where that can't act twice: reads, PUT and DELETE, and creates and merges, which it sends with an Idempotency-Key, the same on every retry.

It lives in the repository (packages/sdk) for now and isn't published to npm yet.

Endpoints#

Every path below starts with /v1. The API reference has each one's parameters, responses and an example.

You#

GET /mewho the token belongs to, its agent and scope, your organizations
GET /tokens, POST /tokens, DELETE /tokens/{id}your tokens; create (web session only) and revoke
GET /ssh-keys, POST /ssh-keys, DELETE /ssh-keys/{id}SSH keys (adding: web session only)
PUT /me/profile, PUT, DELETE /me/avataryour profile and avatar (web session only)
GET /me/following, GET /me/notifications, POST /me/notifications/{id}/read, POST /me/notifications/readwhat you follow, and your notifications; mark one or all read (web session only)
GET /me/identities, DELETE /me/identities/{org}your linked identities; unlink one (web session only)
GET /me/passkeys, PUT, DELETE /me/passkeys/{id}your passkeys: list them, rename one ({"label"}, 1–100 characters), remove one; refused (conflict) for the last way you sign in, upstream_error when Keycloak can't be reached (web session only). Adding one is Keycloak's own page, from Settings
GET /namespaces/{name}, GET /namespaces/{name}/reposa user or organization; its repositories

Organizations#

POST /orgs, GET /orgs/{org}/members, POST /orgs/{org}/members, DELETE /orgs/{org}/members/{username}organizations and their members
PUT /orgs/{org}/profile, PUT, DELETE /orgs/{org}/avataran organization's profile and picture (owners, web session)
PUT, DELETE /orgs/{org}/followfollow or stop following it (web session)
DELETE /orgs/{org}delete it, with all its repositories, for good (owners, web session)
GET, PUT, DELETE /orgs/{org}/sso, POST …/sso/domains, POST …/sso/domains/{domain}/verify, DELETE …/sso/domains/{domain}its single sign-on: the provider, and the domains it verifies (owners, web session)

Repositories#

GET /repos, POST /reposlist (with your access, open changes, last push) and create
GET /repos?affiliation=&owner=&topic=&q=&sort=affiliation: all, mine or following; by owner or topic; found by name or description; sort: name or pushed
POST /repos/{owner}/{name}/forkfork it ({owner, name, description, defaultBranchOnly, visibility})
PUT, DELETE /repos/{owner}/{name}/followfollow it, or stop (web session)
GET /repos/{owner}/{name}branches, tags, clone URLs (cloneUrl, sshUrl), settings
GET /repos/{owner}/{name}/analytics/languagesits languages by bytes at the default branch's head, and the commit they were counted at (the Analytics tab)
DELETE /repos/{owner}/{name}delete it and everything in it, for good (repository admins, signed in to the web UI)
PUT /repos/{owner}/{name}/settingsvisibility, description, topics, default branch, setup commands, install scripts, merge methods; protectedBranches, requireChanges and requiredChecks change the Protected branches ruleset
PUT, DELETE /repos/{owner}/{name}/archive, POST …/transfer ({owner})archive or unarchive it; transfer it (signed in to the web UI)
GET, PUT, DELETE /repos/{owner}/{name}/collaborators[/{username}]collaborators
GET, POST /repos/{owner}/{name}/branches, DELETE …/branches/{branch}, POST …/branches/{branch}/rename, POST …/branches/{branch}/restorethe branches; create, delete, rename, restore one
GET /repos/{owner}/{name}/commits?rev=history, newest first
GET …/commits/{sha}, GET …/commits/{sha}/diffone commit: its message, author, committer and parents; its diff
GET /repos/{owner}/{name}/blame?ref=&path=which commit last changed each line of a file
GET /repos/{owner}/{name}/tree?rev=&path=, /blob?rev=&path=files
POST /imports, GET /imports/{id}, GET /repos/{owner}/{name}/importimports: start one, its status and log
GET, POST /repos/{owner}/{name}/rulesets[?kind=], GET, PUT, DELETE …/rulesets/{id}, POST …/rulesets/release-tagsbranch and tag rulesets (administrators; never agents or apps)
GET …/rulesets/{id}/insightswhat a ruleset in evaluate mode would have refused
GET, POST /repos/{owner}/{name}/mirrors, GET, PUT, DELETE …/mirrors/{id}, POST …/mirrors/{id}/syncmirrors (administrators)
GET /repos/{owner}/{name}/audit?action=, GET /orgs/{org}/audit?repositories=, GET /me/auditthe audit log, newest first; action keeps one action (secret.set) or those starting with it (secret); an organization's has its repositories' entries too, unless repositories=false. Administrators and owners as people, never agents or apps; yours signed in to the web UI
GET, POST /external-tokens, GET, PUT, DELETE /external-tokens/{id}, POST …/{id}/revealyour external tokens (signed in to the web UI)

Changes and reviews#

GET /repos/{owner}/{name}/changes, POST …/changeslist, open ({title, description, source, target})
GET …/changes/{number}the change, its commits, conflicts and review status
GET …/changes/{number}/diffthe textual diff
POST …/changes/{number}/merge, /close, /reopen, /reviewact on it; review re-runs the graph review. An administrator merges past failing required checks with {overrideReason}
GET …/changes/{number}/viewsthe graph review: changeMap and api view documents, checks (rules, plan), renamed, readable names
GET …/changes/{number}/connectionsother repositories: what the change touches, the functions on the other side, breaking, missing and affected findings, each repository's pinned commit
GET …/changes/{number}/conflictsthe conflict map: status (none until the target moves past the merge base), the conflicts document (elements both sides touched, conflicting files and their regions, semantic conflicts), both sides' maps, readable names
GET, POST …/changes/{number}/comments, PUT, DELETE …/comments/{id}the discussion: comments and replies ({body, parentId}); edit or delete your own
POST …/changes/{number}/statusan agent's status on the change ({status, text}: working, pushed, blocked, done)

Checks and runs#

GET, POST /repos/{owner}/{name}/commits/{sha}/checksa commit's checks; report one ({name, status, summary, detailsUrl}; apps need checks:write)
GET …/changes/{number}/checksa change's checks against the required ones: state is passed, pending, failed or none
GET /repos/{owner}/{name}/runs?change=, GET …/runs/{id}workflow runs, newest first, and one run with its jobs
POST …/runs ({workflow, ref}), POST …/runs/{id}/cancel, /rerunrun a workflow by hand, cancel a run, run it again
POST …/runs/{id}/approvegive a run its withheld secrets and identity tokens, so its jobs that need them run (administrators, as people)
POST …/runs/{id}/review ({decision, comment, environments}), POST …/runs/{id}/start-nowapprove or reject the run's deployments waiting for review (listed reviewers); end its wait timers now (administrators); as people
GET …/runs/{id}/artifacts/{artifact}download an artifact, as <name>.tar.gz
GET /repos/{owner}/{name}/actions/caches, DELETE …/actions/caches[?branch=]the repository's caches; clear them, or one branch's (administrators, as people)
GET …/runs/{id}/jobs/{job}/log?after=a job's log from a character offset; ask again from next to follow it until done
GET /orgs/{org}/runners, POST …/runners/registration-tokens, DELETE …/runners/{id}an organization's runners; a registration token (owners, web session); remove one
GET /repos/{owner}/{name}/secrets, PUT, DELETE …/secrets/{secret}; the same under /orgs/{org}/secrets and /repos/{owner}/{name}/environments/{environment}/secretssecrets for runs, by name, and an environment's; values are never returned (people only, never agents or apps)
GET /workflow-schema.jsonthe workflow format's JSON Schema (no sign-in)

Releases and deployments#

GET, POST /repos/{owner}/{name}/releases, GET, PUT, DELETE …/releases/{tag}releases; make one ({tag, target, title, notes, previousTag, draft, prerelease}), change it ({"draft": false} publishes it), delete it; agents and apps only draft
POST …/releases/generate-notes, GET …/releases/{tag}/viewsthe notes offered for a tag; its change map (apps need reviews:read)
GET /repos/{owner}/{name}/compare/{range}/viewsthe change map between two commits, as a new release's (apps need reviews:read)
GET /repos/{owner}/{name}/environments, GET, PUT …/environments/{environment}environments, with what's running in each; set one's required tag, reviewers, Prevent self-review and wait timer ({requiredTag, reviewers, preventSelfReview, waitTimerMinutes}; administrators, never agents)
GET …/environments/{environment}/secrets, PUT, DELETE …/secrets/{secret}an environment's secrets (administrators, as people)
GET, POST /repos/{owner}/{name}/deployments, GET …/deployments/{id}deployments; record one ({environment, ref, status, url, description}; apps need deployments:write); one with its history
POST …/deployments/{id}/statuses, POST …/deployments/{id}/redeployset its status; Deploy again (apps need deployments:write)
POST …/deployments/{id}/review ({decision, comment})approve or reject one waiting for your review (listed reviewers, as people)

Agents and tasks#

GET /repos/{owner}/{name}/agentsthe agent apps installed on the repository
GET, POST /repos/{owner}/{name}/taskstasks for them; hand one out ({agent, instructions, change} or {agent, instructions, base})
GET /tasks, GET /tasks/{id}an agent app's open tasks (a person's: the ones they handed out); one task
POST /tasks/{id}/status, POST /tasks/{id}/cancelthe agent reports ({status, text, change}); a person cancels
GET /orgs/{org}/agentsan organization's agent apps and what they're doing
PUT /orgs/{org}/installations/{slug}an installed app's repositories, maxOpenChanges, and paused

Graphs and views#

GET /repos/{owner}/{name}/graph?ref=a branch's or commit's graph build, with its status
GET …/graphs, POST …/graphs ({ref})builds; start one
GET …/graphs/{commit}the graph as canonical JSON
GET …/views/architecture?ref=&project=the architecture view (inside one project with project)
GET …/views/data-flow?ref=the personal-data flow view
GET /orgs/{org}/graph, GET /users/{username}/graph (?view=&against=)the organization graph: services across an organization's repositories, or a person's
GET, POST /orgs/{org}/graph/views, PUT, DELETE …/graph/views/{slug}; the same under /users/{username}its saved views (changing them: owners, as people)
GET /orgs/{org}/connections/services, GET /users/{username}/connections/servicesthe service names used across the repositories

Webhooks#

GET, POST /repos/{owner}/{name}/webhooksa repository's webhooks; add one (its secret comes back once)
GET, POST /orgs/{org}/webhooksan organization's webhooks
GET, PUT, DELETE /webhooks/{id}a webhook: its URL, events, whether it's active
POST /webhooks/{id}/rotate-secret, POST /webhooks/{id}/pinga new secret (shown once); a test event
GET /webhooks/{id}/deliveries, POST …/deliveries/{delivery}/redeliverthe delivery log; send one again

Integrations#

GET, POST /repos/{owner}/{name}/integrations, GET, POST /orgs/{org}/integrationsa repository's or organization's integrations; add one
GET, PUT, DELETE /integrations/{id}an integration: its service, events, whether it's active
POST /integrations/{id}/test, GET …/deliveries, POST …/deliveries/{delivery}/redelivera test message; the delivery log; send one again
GET, PUT, DELETE /orgs/{org}/linearthe organization's Linear connection; connect with an API key; disconnect
POST /orgs/{org}/linear/oauth, POST …/linear/oauth/callbackconnect with the server's Linear app: where to send the person, then finish with what Linear sent back
PUT …/linear/inbound, GET …/linear/eventsthe signing secret of a Linear webhook made by hand; the last 7 days' events from Linear
PUT …/linear/settings, POST …/linear/refreshits automations and each team's states; read the teams and states again
GET, POST …/changes/{number}/issues, DELETE …/issues/{identifier}a change's linked Linear issues; link or unlink one (write access)
GET /repos/{owner}/{name}/ai-keys, PUT, DELETE …/ai-keys/{app}/{provider}; the same under /orgs/{org}/ai-keysAI provider keys for installed apps (administrators and owners, as people)
POST /installations/ai-keys/{provider}, POST …/{provider}/usagean installed app gets the key for the repository it works on; reports what it spent

Integrations, Linear and AI provider keys are managed by people only, with a session or a personal write token. Linear's events come in at /linear/webhook, outside /v1.

OAuth apps#

GET, POST /apps, GET, PUT, DELETE /apps/{slug}, GET /apps/{slug}/settingsthe OAuth apps you manage; register, change or delete one (web session)
POST /apps/{slug}/rotate-secret, PUT, DELETE, GET /apps/{slug}/icona new client or webhook secret; its icon
GET /authorizations, DELETE /authorizations/{slug}the apps you've authorized to act for you; revoke one
GET, POST /orgs/{org}/installations, PUT, DELETE …/installations/{slug}the apps installed in an organization; install, change or remove one (owners)

Tokens are issued at POST /oauth/token and revoked at POST /oauth/revoke, outside /v1; see OAuth apps.

Chats#

GET, POST /chatsyour chats, newest first, with unread counts; open (or find) the one with someone
GET /chats/{id}, GET, POST /chats/{id}/messagesa conversation; its messages, newest first; send one
PUT, DELETE /chats/{id}/messages/{message}edit or delete your own message
POST /chats/{id}/read, POST /chats/{id}/typingmark it read up to a message; say you're typing
POST /chats/{id}/messages/{message}/reportreport a message to the site administrators
GET /me/blocks, PUT, DELETE /me/blocks/{username}the people you blocked; block or unblock someone
GET /people?q=people to chat with, by username or name
GET /eventsyour live events, as Server-Sent Events

Everything else#

GET /activitypush and merge events you can see
GET /activity/agentsagents' MCP sessions and writes you can see
POST, GET, DELETE /mcpthe MCP server (agents only: OAuth apps and installed agent apps)
GET /guard, PUT /guardpush guard state and deny-all (site admins)
GET /admin/hosted-runners, POST …/registration-tokens, PUT, DELETE …/{id}; GET /admin/hosted-access, PUT, DELETE …/{namespace}hosted runners and who may use them (site admins)
GET /sshthe SSH URL and host key fingerprints
GET /openapi.jsonthis API's OpenAPI document

GET /health (liveness) is outside /v1.

View documents#

Views are JSON documents (kind: "view", schema urn:branchscope:view). Each holds diagrams of:

  • groups: projects, files, types;
  • nodes: with label, kind, status (added, removed, changed, renamed, context), notes and tags;
  • edges: with label, status, whether resolved, and the rule effect and IDs they break.

The web app draws them; agents can read them directly.

Graph documents#

Graphs, diffs and checks are versioned JSON (schema 0.4.x) in canonical form. Their exact shape is specified in docs/graph/SPEC.md in the repository, and their JSON Schemas are in the OpenAPI document.