Phorge HTTP API
Use repositories directly over HTTP without creating a working tree. The API reads files and history, creates commits, and manages refs in focused requests.
Contents
Requests
The base URL is scoped to your namespace:
https://{namespace}.api.phorge.net
Send JSON request bodies with Content-Type: application/json unless an endpoint specifies multipart. Unknown fields and additional JSON values are rejected. Authenticate with a customer-signed bearer token:
Authorization: Bearer <jwt>
A repository name occupies one URL segment. Percent-encode grouped names: team/storefront becomes team%2Fstorefront beneath /repos/. File paths and ref names in path segments must also be percent-encoded.
Authentication
Sign API JWTs with an active ES256 or RS256 private key for your namespace. kid is optional; when present, set it to the key's SHA-256 public-key fingerprint. Without kid, Phorge checks the signature against active keys using the selected algorithm. Phorge does not receive customer private keys.
The dashboard can separately issue a 10-minute, read-only Git clone credential for an authenticated namespace member. That credential works only with Git HTTPS for its selected repository; it is not accepted by this HTTP API.
{
"alg": "ES256",
"kid": "SHA256:…",
"typ": "JWT"
}
{
"iss": "your-org",
"actor": "agent-7",
"repo": "workspace",
"scopes": ["git:read", "repo:write"],
"iat": 1786579200,
"exp": 1786580100,
"refPolicies": ["refs/heads/agent-*" ]
}
iss identifies the namespace, repo is the exact repository name or *, and optional actor labels the caller in request audit records. actor is not a Git author or committer. iat and exp are NumericDate values. refPolicies is optional and restricts writable refs with Git-style patterns.
git:readpermits repository metadata and read endpoints.repo:writepermits repository creation and HTTP writes.org:readwithrepo: "*"permits repository listing.
Reads from a public repository may omit the token. Creation, listing, private reads, and every write require one.
Repositories
POST /repos
Create a repository with a repo:write token scoped to its name. Repeating the same request for a repository you own returns the existing repository with HTTP 200; initial creation returns HTTP 201.
curl --fail-with-body \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{
"name": "workspace",
"ttl_seconds": 86400,
"private": true
}' \
https://your-org.api.phorge.net/repos
Omit ttl_seconds, or set it to 0, for a repository that does not expire. A positive value schedules automatic deletion and must fit within the service limit. Negative values are rejected. private defaults to true.
{
"id": "01J...",
"name": "workspace",
"namespace": "your-org",
"private": true,
"expires_at": "2026-08-14T00:00:00Z",
"size_bytes": 0,
"content_version": 0,
"git_url": "https://your-org.phorge.net/workspace.git",
"api_url": "https://your-org.api.phorge.net/repos/workspace"
}
GET /repos
List repositories using an org:read token with repo: "*". Accepts cursor and limit.
GET /repos/{repo}
Return repository metadata. Requires git:read unless the repository is public.
DELETE /repos/{repo}
Permanently delete a repository and its Git history using a repo:delete token scoped to that repository. A successful deletion returns HTTP 204.
Reads
Unless noted otherwise, read endpoints require git:read, accept ref, and default to HEAD.
GET /repos/{repo}/files
List tree entries. Query parameters:
ref: branch, tag, or commit SHA.path: optional subtree.recursive=false: disable recursive traversal; traversal is recursive by default.metadata=true: include the latest commit for each path.cursorandlimit: pagination.
{
"items": [{
"path": "src/main.ts",
"mode": "100644",
"type": "blob",
"sha": "012345...",
"size": 418
}],
"next_cursor": "",
"has_more": false
}
GET|HEAD /repos/{repo}/files/{path}
Stream a file as application/octet-stream. Accepts ref, a single HTTP Range, and If-None-Match. Responses include ETag and Accept-Ranges: bytes; ranges return HTTP 206.
The SDK's getFileURL method creates a short-lived URL for viewing one private file as plain text in a browser. Its credential is restricted to the selected repository, file path, and ref and does not grant write access.
curl --fail-with-body \
-H "Authorization: Bearer $TOKEN" \
-H "Range: bytes=0-1023" \
"https://your-org.api.phorge.net/repos/workspace/files/src%2Fmain.ts?ref=main"
GET /repos/{repo}/archive
Stream a tar.gz archive. Accepts ref and an optional path.
GET /repos/{repo}/grep
Search repository content. q is required. Also accepts ref, context from 0 through 20, cursor, and limit. Items contain path, line, and text.
GET /repos/{repo}/blame
Return Git line-porcelain blame output as { "porcelain": "..." }. path is required; ref is optional.
GET /repos/{repo}/branches
List branches with name, SHA, date, and commit message. Accepts cursor and limit.
GET /repos/{repo}/commits
List commits. Accepts ref, cursor, and limit. Items contain sha, parents, author, date, and message.
GET /repos/{repo}/commits/{sha}
Return one commit in the same shape used by the commit list.
GET /repos/{repo}/commits/{sha}/diff
Return the commit patch as text/x-diff.
GET /repos/{repo}/diff?base={base}&head={head}
Return a three-dot diff between two revisions as text/x-diff.
Writes
Writes require repo:write. Successful mutations return HTTP 201:
{ "sha": "012345...", "ref": "refs/heads/main" }
Use expectedHeadSha to make concurrent work safe. If the ref changed, Phorge returns HTTP 412 and leaves it unchanged.
POST /repos/{repo}/commits
Create a commit from file operations using multipart/form-data. The first part must be named metadata and contain the JSON commit description. Each upsert names a later raw file part through content_part. Delete operations have no file part. mode may be 100644, 100755, or 120000.
curl --fail-with-body \
-H "Authorization: Bearer $TOKEN" \
-F 'metadata={
"branch":"main",
"expectedHeadSha":"012345...",
"message":"Update worker",
"author":{"name":"Agent","email":"agent@example.com"},
"committer":{"name":"Automation","email":"automation@example.com"},
"operations":[
{"operation":"upsert","path":"src/worker.ts","content_part":"file-0","mode":"100644"},
{"operation":"delete","path":"src/old-worker.ts"}
]
};type=application/json' \
-F 'file-0=@src/worker.ts;type=application/octet-stream' \
https://your-org.api.phorge.net/repos/workspace/commits
File bytes are sent directly and are not Base64 encoded. Part names must be unique and every declared part must be present. The complete multipart request is limited to 128 MiB and still must fit the repository's storage quota.
author and committer are optional. If only author is supplied, it is used for both Git identities. If neither is supplied, Phorge uses Phorge <noreply@phorge.net>. The JWT's optional actor is audit metadata and is never written into a commit.
expectedHeadSha is optional for this endpoint. When omitted, Phorge snapshots the current head and still uses an atomic ref update; supplying it is recommended for coordination between agents.
POST /repos/{repo}/commits/apply-diff
Apply a unified diff and commit it in one request. expectedHeadSha is required.
{
"branch": "main",
"expectedHeadSha": "012345...",
"message": "Apply generated patch",
"diff": "diff --git a/a.txt b/a.txt\n...",
"author": { "name": "Agent", "email": "agent@example.com" }
}
POST /repos/{repo}/branches
Create a branch. startPoint defaults to HEAD; expectedHeadSha must match the resolved start point.
{
"name": "agent-7",
"startPoint": "main",
"expectedHeadSha": "012345..."
}
DELETE /repos/{repo}/branches/{branch}?expectedHeadSha={sha}
Delete a branch only if its current SHA matches expectedHeadSha.
POST /repos/{repo}/merges/preview
Check a merge without moving the target ref. Uses the same body as merge and returns fast_forward, mergeable, result_sha, target_sha, and source_sha.
POST /repos/{repo}/merges
Merge source into target. Mode is fast-forward-only or fast-forward-preferred; the latter is the default. message is optional.
{
"source": "agent-7",
"target": "main",
"expectedHeadSha": "012345...",
"mode": "fast-forward-preferred",
"message": "Merge agent work",
"author": { "name": "Agent", "email": "agent@example.com" }
}
POST /repos/{repo}/tags
Create a lightweight tag, or an annotated tag when message is present. target defaults to HEAD; expectedHeadSha must match the resolved target.
{
"name": "v1.0.0",
"target": "main",
"expectedHeadSha": "012345...",
"message": "Release v1.0.0",
"tagger": { "name": "Release Bot", "email": "releases@example.com" }
}
DELETE /repos/{repo}/tags/{tag}?expectedHeadSha={sha}
Delete a tag only if its current ref SHA matches expectedHeadSha.
Pagination
List endpoints accept limit and cursor. The default limit is 20 and the maximum is 100. Treat cursors as opaque and pass next_cursor unchanged:
{
"items": [],
"next_cursor": "...",
"has_more": true
}
Errors
Errors use an HTTP status plus a stable machine-readable code:
{
"code": "HEAD_MOVED",
"message": "ref has moved",
"details": {
"ref": "refs/heads/main",
"expected": "012345...",
"actual": "abcdef..."
}
}
- HTTP 400: malformed JSON, cursor, path, query input, or revision.
- HTTP 401: missing or invalid token.
- HTTP 403: insufficient scope, ref policy, or quota.
- HTTP 404: repository, file, ref, or route not found.
- HTTP 409: merge conflict or non-fast-forward merge.
- HTTP 412
HEAD_MOVED: the expected ref no longer matches. - HTTP 413
COMMIT_TOO_LARGE: a multipart commit exceeds 128 MiB. - HTTP 415
MULTIPART_REQUIRED: a file-operation commit was not sent as multipart. - HTTP 416
INVALID_RANGE: the requested byte range is invalid. - HTTP 422: a repository name, ref, commit operation, diff, or merge input is invalid.
- HTTP 428
EXPECTED_HEAD_REQUIRED: a required expected SHA was omitted. - HTTP 429
RATE_LIMITED: request rate exceeded.