Repositories
Reference for the repository endpoints under /api/v1/repos: creating, reading,
updating, and administering repositories along with their visibility tier,
collaborators, branches, tags, and file contents.
Resource overview
A repository is identified by its owner username and name. The collection lives
at /api/v1/repos, and every per-repository operation hangs off
/api/v1/repos/{owner}/{repo}/.... This page covers:
- the repository record — list, fetch, create, update, delete;
- visibility — the
public/internal/privatetier that gates who can read a repository; - collaborators — the per-repository access list and effective permissions;
- branches and tags — listing git refs and creating or deleting them;
- contents, raw bytes, and blame — reading files and directory listings at a ref;
- transfer — moving a repository to another owner (an instance-administration operation).
Resources that read as part of a repository but are documented elsewhere: merge requests on merges.md, issues on issues.md, pipelines and CI/CD on cicd.md, and packages under registry.
Visibility tiers
Every repository has one of three visibility tiers, which decides who may read it:
| Tier | Who can read |
|---|---|
public |
Anyone, including unauthenticated callers. |
internal |
Any authenticated user on the instance. |
private |
The owner, instance administrators, and collaborators that hold repository read access. |
Read endpoints enforce this tier. A private repository the caller may not read
returns 404 Not Found — never 403 — so its existence is not disclosed; see
the 404-not-403 rule in errors.md. internal and public
repositories are visible to the audiences their tier defines.
The visibility field is the canonical representation. The boolean is_private
is a deprecated shim that equals visibility == "private"; it is still accepted
on write and emitted on read, but new integrations should use visibility.
Auth & scopes
See conventions.md for the accepted credential types and how
scopes are enforced. Read endpoints on public repositories need no credential;
all other access carries a bearer token. The operations on this page require:
| Operation | OAuth2 scope | PAT scope |
|---|---|---|
| Read | read:repo |
repo:read |
| Write | write:repo |
repo:write |
| Administer | admin:repo |
repo:admin |
Scope is enforced additively with the underlying access check, so the per-caller authorization noted on each endpoint below still applies. Two operations are narrower than the scope table suggests:
- Updating repository metadata and deleting a repository are restricted to the owner or an instance administrator. A collaborator with write access can push refs but cannot rename, re-tier, or delete the repository.
- Repository transfer is an instance-administration operation; it requires
an administrator holding the
admin:repo_transferadmin scope and is gated behind thetransfer.enabledsetting.
Endpoints
| Method | Path | Summary |
|---|---|---|
GET |
/api/v1/repos |
List repositories the caller can see |
GET |
/api/v1/repos/{owner}/{repo} |
Fetch a repository |
POST |
/api/v1/repos |
Create a repository |
PATCH |
/api/v1/repos/{owner}/{repo} |
Update a repository |
DELETE |
/api/v1/repos/{owner}/{repo} |
Delete a repository |
GET |
/api/v1/repos/{owner}/{repo}/contents/{path} |
Read a file or directory listing at a ref |
GET |
/api/v1/repos/{owner}/{repo}/raw/{ref}/{path} |
Download raw file bytes |
GET |
/api/v1/repos/{owner}/{repo}/blame/{ref}/{path} |
Line-by-line blame for a file |
GET |
/api/v1/repos/{owner}/{repo}/branches |
List branches (paginated) |
POST |
/api/v1/repos/{owner}/{repo}/branches |
Create a branch |
DELETE |
/api/v1/repos/{owner}/{repo}/branches/{branch} |
Delete a branch |
GET |
/api/v1/repos/{owner}/{repo}/tags |
List tags (paginated) |
POST |
/api/v1/repos/{owner}/{repo}/tags |
Create a tag |
DELETE |
/api/v1/repos/{owner}/{repo}/tags/{tag} |
Delete a tag |
GET |
/api/v1/repos/{owner}/{repo}/collaborators |
List collaborators |
PUT |
/api/v1/repos/{owner}/{repo}/collaborators/{user} |
Add or update a collaborator |
GET |
/api/v1/repos/{owner}/{repo}/collaborators/{user}/permissions |
Inspect a collaborator's effective permissions |
DELETE |
/api/v1/repos/{owner}/{repo}/collaborators/{user} |
Remove a collaborator |
POST |
/api/v1/admin/repos/{owner}/{repo}/transfer/preflight |
Preview a repository transfer |
POST |
/api/v1/admin/repos/{owner}/{repo}/transfer |
Start a repository transfer |
GET |
/api/v1/admin/transfers |
List repository transfers (paginated) |
GET |
/api/v1/admin/transfers/{id} |
Fetch one repository transfer |
GET /api/v1/repos
List repositories visible to the caller, most recently updated first.
Unauthenticated callers see public repositories only; authenticated callers
also see internal repositories. This endpoint returns a plain JSON array (not
the page envelope) and is capped server-side.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
mine |
boolean | false |
When true, return only repositories owned by the caller (requires authentication). Capped at 100. |
owner |
string | — | Restrict the listing to one owner's repositories visible to the caller. Capped at 50. |
With neither parameter the listing is the instance-wide visible set, capped at 50.
Response
[
{
"id": "9f1c2e7a-...",
"owner": "alice",
"owner_id": "3b8e...",
"name": "widgets",
"description": "Widget service",
"is_private": false,
"visibility": "public",
"default_branch": "main",
"clone_url_http": "https://api.gitvetrix.com/alice/widgets.git",
"clone_url_ssh": "git@gitvetrix.com:alice/widgets.git",
"created_at": "2026-01-04T12:00:00Z",
"updated_at": "2026-02-19T09:31:00Z"
}
]
Status codes
| Status | When |
|---|---|
200 OK |
Listing returned. |
401 Unauthorized |
mine=true without a valid session. |
Example
curl -H "Authorization: Bearer <token>" \
"https://api.gitvetrix.com/api/v1/repos?mine=true"
GET /api/v1/repos/{owner}/{repo}
Fetch a single repository's metadata. Subject to the visibility tier.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
Response
{
"id": "9f1c2e7a-...",
"owner": "alice",
"owner_id": "3b8e...",
"name": "widgets",
"description": "Widget service",
"is_private": false,
"visibility": "public",
"default_branch": "main",
"issue_code": "WIDG",
"clone_url_http": "https://api.gitvetrix.com/alice/widgets.git",
"clone_url_ssh": "git@gitvetrix.com:alice/widgets.git",
"stars_count": 12,
"starred": false,
"forks_count": 0,
"watchers_count": 3,
"watching": false,
"open_issues_count": 5,
"open_prs_count": 1,
"created_at": "2026-01-04T12:00:00Z",
"updated_at": "2026-02-19T09:31:00Z"
}
Key fields: visibility is the canonical tier and is_private its deprecated
mirror; issue_code is the per-repository project key used to number issues;
clone_url_http and clone_url_ssh are rendered from the instance's configured
external URLs; starred and watching reflect the calling user's own state and
are always false for anonymous callers.
The response carries an ETag and Cache-Control: private, must-revalidate, max-age=0. A request whose If-None-Match matches the current entity is
answered 304 Not Modified with no body.
Status codes
| Status | When |
|---|---|
200 OK |
Repository returned. |
304 Not Modified |
If-None-Match matches the current ETag. |
404 Not Found |
Repository does not exist, or is private and not readable — see Errors. |
Example
curl https://api.gitvetrix.com/api/v1/repos/alice/widgets
POST /api/v1/repos
Create a repository owned by the authenticated user.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Repository name. |
description |
string | no | Free-text description. |
visibility |
string | no | public, internal, or private. |
is_private |
boolean | no | Deprecated shim: true maps to private, false to public. Ignored when visibility is set. |
default_branch |
string | no | Initial branch name. Defaults to main. |
Visibility is secure-by-default: when neither visibility nor is_private is
supplied the repository is created private. An explicit visibility always
wins over is_private.
{
"name": "widgets",
"description": "Widget service",
"visibility": "internal",
"default_branch": "main"
}
Response
201 Created with the repository record (the same shape as the fetch response
above).
Status codes
| Status | When |
|---|---|
201 Created |
Repository created. |
400 Bad Request |
Invalid body, or name is missing. |
401 Unauthorized |
No valid session. |
409 Conflict |
A repository with that name already exists for the owner. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"widgets","visibility":"internal"}' \
https://api.gitvetrix.com/api/v1/repos
PATCH /api/v1/repos/{owner}/{repo}
Update repository metadata. Restricted to the owner or an instance administrator. Every field is optional; only the fields supplied are changed.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
Request body
| Field | Type | Description |
|---|---|---|
name |
string | New name. Renaming moves the repository's storage in lockstep. |
description |
string | New description. |
visibility |
string | public, internal, or private. |
is_private |
boolean | Deprecated shim; derives visibility when visibility is absent. |
default_branch |
string | New default branch. |
issue_code |
string | Project key: 2–10 uppercase alphanumerics starting with a letter, or empty to clear. |
{
"visibility": "private",
"issue_code": "WIDG"
}
Response
200 OK with the updated repository record.
Status codes
| Status | When |
|---|---|
200 OK |
Repository updated. |
400 Bad Request |
Invalid body, an unrecognized visibility, or a malformed issue_code. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller is neither the owner nor an instance administrator. |
404 Not Found |
Repository does not exist, or is private and not readable. |
409 Conflict |
The new name is already in use, or the issue_code is already taken by another repository. A name collision returns { "error": "name_in_use", "existing_repo_id": "<uuid>" }. |
Example
curl -X PATCH -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"visibility":"private"}' \
https://api.gitvetrix.com/api/v1/repos/alice/widgets
DELETE /api/v1/repos/{owner}/{repo}
Delete a repository and its git storage. Restricted to the owner or an instance
administrator. The request body must confirm the full owner/repo slug exactly
(case-sensitive) to guard against deleting the wrong repository.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
Request body
{ "confirmation": "alice/widgets" }
Status codes
| Status | When |
|---|---|
204 No Content |
Repository deleted. |
400 Bad Request |
Body missing, or confirmation does not equal <owner>/<repo> exactly. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller is neither the owner nor an instance administrator. |
404 Not Found |
Repository does not exist, or is private and not readable. |
Example
curl -X DELETE -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"confirmation":"alice/widgets"}' \
https://api.gitvetrix.com/api/v1/repos/alice/widgets
GET /api/v1/repos/{owner}/{repo}/contents/{path}
Read what lives at path in the repository at a ref. A directory returns a JSON
array of entries; a file returns a single blob object. Omit path (or use the
bare /contents form) to read the repository root.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
path |
string | Path within the tree. Empty for the repository root. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
ref |
string | HEAD |
Branch, tag, or commit SHA to read from. |
Response
A directory listing:
[
{
"name": "README.md",
"path": "README.md",
"type": "file",
"size": 1024,
"mode": "100644",
"last_commit_message": "Add README",
"last_commit_date": "2026-02-19T09:31:00Z"
}
]
A file blob:
{
"path": "README.md",
"encoding": "utf-8",
"content": "# Widgets\n...",
"is_binary": false,
"lang": "markdown",
"size": 1024
}
The response carries an ETag; a matching If-None-Match is answered
304 Not Modified.
Status codes
| Status | When |
|---|---|
200 OK |
Directory listing or file blob returned. |
304 Not Modified |
If-None-Match matches the current ETag. |
404 Not Found |
Repository not readable, or no such path at the ref. |
Example
curl "https://api.gitvetrix.com/api/v1/repos/alice/widgets/contents/src?ref=main"
GET /api/v1/repos/{owner}/{repo}/raw/{ref}/{path}
Serve a file's raw bytes with a content type derived from its extension. Binary
files are sent with Content-Disposition: attachment; X-Content-Type-Options: nosniff is always set.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
ref |
string | Branch, tag, or commit SHA. |
path |
string | File path within the tree. |
Status codes
| Status | When |
|---|---|
200 OK |
File bytes returned. |
404 Not Found |
Repository not readable, or no such file at the ref. |
Example
curl https://api.gitvetrix.com/api/v1/repos/alice/widgets/raw/main/README.md
GET /api/v1/repos/{owner}/{repo}/blame/{ref}/{path}
Return per-line blame for a file: each line with the commit and author that last changed it.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
ref |
string | Branch, tag, or commit SHA. |
path |
string | File path within the tree. |
Response
[
{
"line_number": 1,
"content": "# Widgets",
"sha": "a1b2c3d4...",
"author_name": "Alice",
"author_email": "alice@example.com",
"author_date": "2026-01-04T12:00:00Z"
}
]
Status codes
| Status | When |
|---|---|
200 OK |
Blame returned. |
404 Not Found |
Repository not readable, or the file or ref does not exist. |
Example
curl https://api.gitvetrix.com/api/v1/repos/alice/widgets/blame/main/README.md
GET /api/v1/repos/{owner}/{repo}/branches
List branches, paginated. Subject to the visibility tier: a private
repository requires read access (401 for anonymous callers; 404 for an
authenticated non-member), and an internal repository requires authentication.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
See Pagination. |
per_page |
integer | 25 |
Items per page, clamped to a maximum of 100. |
include_divergence |
boolean | false |
When true, compute each branch's ahead/behind counts relative to the default branch. The default listing leaves these at 0. |
sort |
string | — | One of name, last_commit_at, ahead, behind. |
order |
string | asc |
asc or desc. Only honored alongside sort. |
Response
{
"items": [
{
"name": "main",
"commit_sha": "a1b2c3d4...",
"commit_message": "Add README",
"commit_author": "Alice",
"commit_date": "2026-02-19T09:31:00Z",
"is_default": true,
"is_protected": true,
"ahead_count": 0,
"behind_count": 0
}
],
"total": 7,
"page": 1,
"per_page": 25
}
ahead_count and behind_count are 0 unless include_divergence=true. The
response carries an ETag derived from the branch tips; a matching
If-None-Match is answered 304 Not Modified.
Status codes
| Status | When |
|---|---|
200 OK |
Page returned. |
304 Not Modified |
If-None-Match matches the current ETag. |
400 Bad Request |
Invalid sort or order. |
401 Unauthorized |
Anonymous request against a private repository. |
404 Not Found |
Repository not readable. |
Example
curl "https://api.gitvetrix.com/api/v1/repos/alice/widgets/branches?per_page=50"
POST /api/v1/repos/{owner}/{repo}/branches
Create a branch. Requires repository write access.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | New branch name. |
base_ref |
string | no | Branch or commit SHA to branch from. Defaults to the repository's default branch. |
{ "name": "feature/x", "base_ref": "main" }
Response
{ "name": "feature/x", "base_ref": "main" }
Status codes
| Status | When |
|---|---|
201 Created |
Branch created. |
400 Bad Request |
Invalid body, missing name, or an invalid base ref. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller lacks write access. |
404 Not Found |
Repository not readable. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"feature/x","base_ref":"main"}' \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/branches
DELETE /api/v1/repos/{owner}/{repo}/branches/{branch}
Delete a branch. Requires repository write access. The default branch cannot be
deleted, and a branch matching a protected-branch rule is refused. Open merge
requests that target the branch are closed as a side effect. Percent-encode /
in branch names as %2F.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
branch |
string | Branch name (the path after /branches/). |
Status codes
| Status | When |
|---|---|
204 No Content |
Branch deleted. |
400 Bad Request |
The branch is the repository's default branch. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller lacks write access, or the branch is protected. |
404 Not Found |
Repository or branch not found. |
429 Too Many Requests |
The per-actor branch-delete rate limit was exceeded — see Rate limits. |
503 Service Unavailable |
The audit record for the deletion could not be written. The audit row is committed before the branch is dropped, so a failed write refuses the delete rather than letting it succeed unaudited. |
Example
curl -X DELETE -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/branches/feature%2Fx
GET /api/v1/repos/{owner}/{repo}/tags
List tags, paginated. Subject to the same visibility enforcement as the branch list.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
See Pagination. |
per_page |
integer | 25 |
Items per page, clamped to a maximum of 100. |
Response
{
"items": [
{
"name": "v1.0.0",
"commit_sha": "a1b2c3d4...",
"message": "Release 1.0.0",
"tagger_name": "Alice",
"tagger_email": "alice@example.com",
"tagged_at": "2026-01-04T12:00:00Z",
"is_annotated": true
}
],
"total": 3,
"page": 1,
"per_page": 25
}
Status codes
| Status | When |
|---|---|
200 OK |
Page returned. |
401 Unauthorized |
Anonymous request against a private repository. |
404 Not Found |
Repository not readable. |
Example
curl "https://api.gitvetrix.com/api/v1/repos/alice/widgets/tags"
POST /api/v1/repos/{owner}/{repo}/tags
Create a tag. Requires repository write access. An empty message creates a
lightweight tag; a non-empty message creates an annotated tag.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Tag name. |
ref |
string | no | Branch, commit SHA, or existing tag to point at. Defaults to the default branch. |
message |
string | no | Annotation message. Empty for a lightweight tag. |
{ "name": "v1.0.0", "ref": "main", "message": "Release 1.0.0" }
Response
{ "name": "v1.0.0", "ref": "main" }
Status codes
| Status | When |
|---|---|
201 Created |
Tag created. |
400 Bad Request |
Invalid body, missing name, or an invalid ref. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller lacks write access. |
404 Not Found |
Repository not readable. |
409 Conflict |
A tag with that name already exists. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"v1.0.0","ref":"main","message":"Release 1.0.0"}' \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/tags
DELETE /api/v1/repos/{owner}/{repo}/tags/{tag}
Delete a tag. Requires repository write access. Percent-encode / in tag names
as %2F.
Path parameters
| Name | Type | Description |
|---|---|---|
owner |
string | Owner username. |
repo |
string | Repository name. |
tag |
string | Tag name (the path after /tags/). |
Status codes
| Status | When |
|---|---|
204 No Content |
Tag deleted. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller lacks write access. |
404 Not Found |
Repository or tag not found. |
Example
curl -X DELETE -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/tags/v1.0.0
GET /api/v1/repos/{owner}/{repo}/collaborators
List a repository's collaborators with their role and effective permissions.
Requires repository read access; a private repository resolves membership-only,
so an authenticated non-member receives 404. Returns a JSON array.
Response
[
{
"user": {
"id": "3b8e...",
"username": "bob",
"display_name": "Bob",
"avatar_url": "/user-content/.../avatar.jpg"
},
"role": "write",
"permissions": ["repo:read", "repo:write"],
"created_at": "2026-02-01T08:00:00Z"
}
]
The short role names are read, triage, write, maintain, and admin.
Status codes
| Status | When |
|---|---|
200 OK |
Collaborator list returned. |
404 Not Found |
Repository not readable. |
Example
curl -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/collaborators
PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}
Add a collaborator or change an existing one's role. Permitted to the repository
owner, an instance administrator, a collaborator holding the admin role on the
repository, or a caller with the repo_roles admin scope.
Supply either an explicit role (with optional permissions) or a
role_template_id to snapshot a role template's permissions onto the
collaborator.
Path parameters
| Name | Type | Description |
|---|---|---|
user |
string | Username of the collaborator to add or update. |
Request body
| Field | Type | Description |
|---|---|---|
role |
string | Role to assign: read, triage, write, maintain, admin, or a custom role. |
permissions |
string[] | Explicit permission list for a custom role. |
role_template_id |
string | UUID of a role template to snapshot. Mutually exclusive with role. |
{ "role": "write" }
Status codes
| Status | When |
|---|---|
204 No Content |
Collaborator added or updated. |
400 Bad Request |
Invalid JSON, an unknown role, or a malformed role_template_id. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller may not manage collaborators, or the role template belongs to another repository. |
404 Not Found |
Repository, target user, or role template not found. |
503 Service Unavailable |
A role_template_id was supplied but role templates are not configured on this deployment. |
Example
curl -X PUT -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"role":"write"}' \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/collaborators/bob
GET /api/v1/repos/{owner}/{repo}/collaborators/{user}/permissions
Return one collaborator's resolved access. Requires repository read access.
Path parameters
| Name | Type | Description |
|---|---|---|
user |
string | Collaborator username. |
Response
{
"role": "repo_write",
"permissions": ["repo:read", "repo:write"],
"source": "role",
"role_template_id": "f0e1..."
}
source is role, template, or custom. role_template_id is present only
when the access was snapshotted from a role template.
Status codes
| Status | When |
|---|---|
200 OK |
Effective permissions returned. |
404 Not Found |
Repository not readable, the user does not exist, or the user is not a collaborator. |
Example
curl -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/collaborators/bob/permissions
DELETE /api/v1/repos/{owner}/{repo}/collaborators/{user}
Remove a collaborator. Restricted to the repository owner or an instance administrator.
Path parameters
| Name | Type | Description |
|---|---|---|
user |
string | Collaborator username. |
Status codes
| Status | When |
|---|---|
204 No Content |
Collaborator removed. |
401 Unauthorized |
No valid session. |
403 Forbidden |
Caller is neither the owner nor an instance administrator. |
404 Not Found |
Repository or target user not found. |
Example
curl -X DELETE -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/repos/alice/widgets/collaborators/bob
POST /api/v1/admin/repos/{owner}/{repo}/transfer/preflight
Preview a repository transfer to another owner without changing anything.
Instance-administration: requires an administrator with the repo_transfer
admin scope, and returns 404 when the transfer.enabled setting is off.
Request body
{ "target_username": "carol" }
Response
{
"target_exists": true,
"source_exists": true,
"name_collision_at_target": false,
"collaborators_to_drop": [],
"group_grants_to_drop": []
}
collaborators_to_drop and group_grants_to_drop list the access entries that
the transfer would remove. in_flight_transfer and existing_colliding_repo_id
appear only when relevant.
Status codes
| Status | When |
|---|---|
200 OK |
Preflight result returned. |
400 Bad Request |
Missing or unknown body fields, or an empty target_username. |
401 Unauthorized |
No valid session. |
404 Not Found |
Source repository or target user not found, or the feature is disabled. |
409 Conflict |
The repository name already exists under the target owner. |
412 Precondition Failed |
A precondition is unmet, such as no configured backup destination. |
403 Forbidden |
Caller lacks the admin:repo_transfer admin scope. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"target_username":"carol"}' \
https://api.gitvetrix.com/api/v1/admin/repos/alice/widgets/transfer/preflight
POST /api/v1/admin/repos/{owner}/{repo}/transfer
Start a repository transfer. The work runs asynchronously; the response returns
the transfer's id and initial state, and progress is observed through the
transfer history endpoints. Requires the admin:repo_transfer admin scope and the
transfer.enabled setting. confirm_repo_name must match the repository name.
Request body
{ "target_username": "carol", "confirm_repo_name": "widgets" }
Response
{ "transfer_id": "d4c3...", "state": "queued" }
Status codes
| Status | When |
|---|---|
202 Accepted |
Transfer started; poll the history endpoints for terminal state. |
400 Bad Request |
Missing or unknown body fields, or confirm_repo_name does not match. |
401 Unauthorized |
No valid session. |
404 Not Found |
Source repository or target user not found, or the feature is disabled. |
409 Conflict |
A transfer for the same source repository is already in flight. |
403 Forbidden |
Caller lacks the admin:repo_transfer admin scope. |
Example
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"target_username":"carol","confirm_repo_name":"widgets"}' \
https://api.gitvetrix.com/api/v1/admin/repos/alice/widgets/transfer
GET /api/v1/admin/transfers
List repository transfers, most recent first, paginated. Requires the
admin:repo_transfer admin scope and the transfer.enabled setting.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
See Pagination. |
per_page |
integer | 25 |
Items per page, clamped to a maximum of 100. |
target_user |
string | — | Filter by target owner username (case-insensitive). An unknown username yields an empty page. |
state |
string | — | Filter by transfer state. A malformed value returns 400. |
Response
{
"items": [
{
"transfer_id": "d4c3...",
"source_user_id": "3b8e...",
"target_user_id": "7a21...",
"source_repo_id": "9f1c...",
"state": "completed",
"created_at": "2026-02-19T09:31:00Z",
"completed_at": "2026-02-19T09:33:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 25
}
Status codes
| Status | When |
|---|---|
200 OK |
Page returned. |
400 Bad Request |
Invalid state. |
404 Not Found |
The feature is disabled. |
403 Forbidden |
Caller lacks the admin:repo_transfer admin scope. |
Example
curl -H "Authorization: Bearer <token>" \
"https://api.gitvetrix.com/api/v1/admin/transfers?state=completed"
GET /api/v1/admin/transfers/{id}
Fetch one transfer's full record, including backup paths and the manifest
summary. Requires the admin:repo_transfer admin scope and the transfer.enabled
setting.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string | UUID of the transfer. |
Status codes
| Status | When |
|---|---|
200 OK |
Transfer returned. |
400 Bad Request |
The id is not a valid UUID. |
404 Not Found |
No transfer with that id, or the feature is disabled. |
403 Forbidden |
Caller lacks the admin:repo_transfer admin scope. |
Example
curl -H "Authorization: Bearer <token>" \
https://api.gitvetrix.com/api/v1/admin/transfers/d4c3...
Errors
These endpoints use the shared error envelope and status-code conventions in
errors.md, including the 404-not-403 rule that hides private
repositories. Codes and bodies specific to this resource:
- A repository rename collision returns
409with the structured body{ "error": "name_in_use", "existing_repo_id": "<uuid>" }. - Transfer endpoints classify failures with stable
error_codevalues, among themsource_repo_not_found,target_user_not_found,name_collision_at_target,in_flight_transfer, and a412for an unmet precondition such as a missing backup destination. When thetransfer.enabledsetting is off, every transfer route answers404.
Rate limits
These endpoints are metered under the standard scopes described in
rate-limits.md: read operations under api.read and
create/update/delete operations under api.write. None are metered as
api.expensive.
Branch deletion carries an additional per-actor budget on top of api.write: a
caller is capped at a fixed number of branch deletions per rolling window
(default 30 per 5 minutes), and exceeding it returns 429 with the standard
rate-limit headers.