# Forgejo API — Repository Endpoints

Base URL: `https://git.cleverthis.com/api/v1`

All requests require authentication via `Authorization: token ${FORGEJO_PAT}` header.

---

## Quick Reference Table

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/repos/search` | Search repositories |
| `POST` | `/user/repos` | Create repo for authenticated user |
| `POST` | `/org/{org}/repos` | Create repo in organization |
| `POST` | `/repos/migrate` | Migrate repository from external source |
| `GET` | `/repos/{owner}/{repo}` | Get repository details |
| `PATCH` | `/repos/{owner}/{repo}` | Edit repository settings |
| `DELETE` | `/repos/{owner}/{repo}` | Delete repository |
| `GET` | `/repositories/{id}` | Get repo by numeric ID |
| `POST` | `/{template_owner}/{template_repo}/generate` | Create repo from template |
| `GET` | `/repos/{owner}/{repo}/raw/{filepath}` | Get raw file content |
| `GET` | `/repos/{owner}/{repo}/archive/{archive}` | Download archive (.zip, .tar.gz) |
| `GET` | `/repos/{owner}/{repo}/topics` | List topics |
| `PUT` | `/repos/{owner}/{repo}/topics` | Replace all topics |
| `PUT` | `/repos/{owner}/{repo}/topics/{topic}` | Add a topic |
| `DELETE` | `/repos/{owner}/{repo}/topics/{topic}` | Remove a topic |
| `GET` | `/topics/search` | Search topics |
| `GET` | `/repos/{owner}/{repo}/collaborators` | List collaborators |
| `GET` | `/repos/{owner}/{repo}/collaborators/{collaborator}` | Check collaborator status |
| `PUT` | `/repos/{owner}/{repo}/collaborators/{collaborator}` | Add collaborator |
| `DELETE` | `/repos/{owner}/{repo}/collaborators/{collaborator}` | Remove collaborator |
| `GET` | `/repos/{owner}/{repo}/collaborators/{collaborator}/permission` | Get permission level |
| `GET` | `/repos/{owner}/{repo}/assignees` | List available assignees |
| `GET` | `/repos/{owner}/{repo}/reviewers` | List available reviewers |
| `GET` | `/repos/{owner}/{repo}/teams` | List teams with access |
| `GET` | `/repos/{owner}/{repo}/teams/{team}` | Check team access |
| `PUT` | `/repos/{owner}/{repo}/teams/{team}` | Add team |
| `DELETE` | `/repos/{owner}/{repo}/teams/{team}` | Remove team |
| `GET` | `/repos/{owner}/{repo}/forks` | List forks |
| `POST` | `/repos/{owner}/{repo}/forks` | Create fork |
| `GET` | `/repos/{owner}/{repo}/sync_fork` | Check fork sync status |
| `POST` | `/repos/{owner}/{repo}/sync_fork` | Sync fork with upstream |
| `GET` | `/repos/{owner}/{repo}/sync_fork/{branch}` | Check branch sync status |
| `POST` | `/repos/{owner}/{repo}/sync_fork/{branch}` | Sync specific branch |
| `POST` | `/repos/{owner}/{repo}/transfer` | Transfer repository |
| `POST` | `/repos/{owner}/{repo}/transfer/accept` | Accept transfer |
| `POST` | `/repos/{owner}/{repo}/transfer/reject` | Reject transfer |
| `POST` | `/repos/{owner}/{repo}/mirror-sync` | Sync mirror |
| `GET` | `/repos/{owner}/{repo}/push_mirrors` | List push mirrors |
| `POST` | `/repos/{owner}/{repo}/push_mirrors` | Add push mirror |
| `DELETE` | `/repos/{owner}/{repo}/push_mirrors/{name}` | Delete push mirror |
| `POST` | `/repos/{owner}/{repo}/push_mirrors-sync` | Sync all push mirrors |
| `POST` | `/repos/{owner}/{repo}/convert` | Convert fork to standalone |
| `GET` | `/repos/{owner}/{repo}/stargazers` | List stargazers |
| `GET` | `/repos/{owner}/{repo}/subscribers` | List watchers |
| `GET` | `/repos/{owner}/{repo}/subscription` | Check watch status |
| `PUT` | `/repos/{owner}/{repo}/subscription` | Watch repo |
| `DELETE` | `/repos/{owner}/{repo}/subscription` | Unwatch repo |
| `GET` | `/user/starred` | List repos starred by current user |
| `GET` | `/user/starred/{owner}/{repo}` | Check if starred |
| `PUT` | `/user/starred/{owner}/{repo}` | Star repo |
| `DELETE` | `/user/starred/{owner}/{repo}` | Unstar repo |
| `GET` | `/user/subscriptions` | List repos watched by current user |
| `GET` | `/repos/{owner}/{repo}/hooks` | List webhooks |
| `POST` | `/repos/{owner}/{repo}/hooks` | Create webhook |
| `GET` | `/repos/{owner}/{repo}/hooks/{id}` | Get webhook |
| `PATCH` | `/repos/{owner}/{repo}/hooks/{id}` | Edit webhook |
| `DELETE` | `/repos/{owner}/{repo}/hooks/{id}` | Delete webhook |
| `POST` | `/repos/{owner}/{repo}/hooks/{id}/tests` | Test webhook |
| `GET` | `/repos/{owner}/{repo}/signing-key.gpg` | Get signing key |
| `GET` | `/repos/{owner}/{repo}/languages` | Get language statistics |
| `GET` | `/repos/{owner}/{repo}/activities/feeds` | Get activity feeds |
| `GET` | `/repos/{owner}/{repo}/statuses/{sha}` | List commit statuses |
| `POST` | `/repos/{owner}/{repo}/statuses/{sha}` | Create commit status |
| `GET` | `/repos/{owner}/{repo}/commits/{ref}/status` | Get combined status |

---

## Repository Model

Key fields returned in repository objects:

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique numeric ID |
| `name` | string | Repository name |
| `full_name` | string | `owner/name` format |
| `description` | string | Repository description |
| `empty` | boolean | Whether the repo has no content |
| `private` | boolean | Whether the repo is private |
| `fork` | boolean | Whether this is a fork |
| `template` | boolean | Whether this is a template repo |
| `mirror` | boolean | Whether this is a mirror |
| `size` | integer | Size in KB |
| `language` | string | Primary language |
| `default_branch` | string | Default branch name |
| `open_issues_count` | integer | Number of open issues |
| `open_pr_counter` | integer | Number of open pull requests |
| `stars_count` | integer | Number of stars |
| `watchers_count` | integer | Number of watchers |
| `forks_count` | integer | Number of forks |
| `topics` | array[string] | Topic tags |
| `archived` | boolean | Whether the repo is archived |
| `has_issues` | boolean | Issues enabled |
| `has_wiki` | boolean | Wiki enabled |
| `has_pull_requests` | boolean | PRs enabled |
| `has_actions` | boolean | Actions enabled |
| `has_releases` | boolean | Releases enabled |
| `has_packages` | boolean | Packages enabled |
| `has_projects` | boolean | Projects enabled |
| `allow_merge_commits` | boolean | Merge commits allowed |
| `allow_rebase` | boolean | Rebase merging allowed |
| `allow_rebase_explicit` | boolean | Rebase + merge commit allowed |
| `allow_squash_merge` | boolean | Squash merging allowed |
| `allow_rebase_update` | boolean | Rebase to update branches allowed |
| `default_merge_style` | string | Default merge style |
| `default_delete_branch_after_merge` | boolean | Auto-delete head branch |
| `permissions` | object | Current user's permissions (`admin`, `push`, `pull`) |
| `clone_url` | string | HTTPS clone URL |
| `ssh_url` | string | SSH clone URL |
| `html_url` | string | Web UI URL |
| `created_at` | string | ISO 8601 creation timestamp |
| `updated_at` | string | ISO 8601 last update timestamp |
| `owner` | object | Owner user/org object |
| `parent` | object | Parent repo (if fork) |
| `internal` | boolean | Whether the repo is internal visibility |
| `avatar_url` | string | Repository avatar URL |
| `link` | string | API URL |

---

## Core Endpoints

### Search Repositories

```
GET /repos/search
```

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `q` | string | Search query keyword |
| `topic` | boolean | If `true`, search topics only |
| `includeDesc` | boolean | Include description in search (default: `true`) |
| `uid` | integer | Filter by owner user ID |
| `priority_owner_id` | integer | Prioritize repos owned by this user ID |
| `team_id` | integer | Filter by team ID |
| `starredBy` | integer | Filter by user ID who starred |
| `private` | boolean | Include private repos (requires auth) |
| `is_private` | boolean | Show only private repos |
| `template` | boolean | Filter template repos |
| `archived` | boolean | Filter archived repos |
| `mode` | string | Search mode: `""`, `"source"`, `"fork"`, `"mirror"`, `"collaborative"` |
| `exclusive` | boolean | If `true`, match mode exactly |
| `sort` | string | Sort: `"alpha"`, `"created"`, `"updated"`, `"size"`, `"id"`, `"stars"`, `"forks"` |
| `order` | string | `"asc"` or `"desc"` |
| `page` | integer | Page number (1-based) |
| `limit` | integer | Page size (max varies by server config) |

```bash
# Search for repos by keyword
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?q=myproject&limit=10"

# Search private repos only
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?q=secret&is_private=true&limit=5"

# Search by topic
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?q=golang&topic=true"

# Search sorted by stars, descending
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?q=web&sort=stars&order=desc&limit=20"

# Search forks only
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?q=linux&mode=fork"

# Search repos starred by user ID 5
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/search?starredBy=5&limit=50"
```

**Response:** `200 OK` — `{ "ok": true, "data": [ {Repository}, ... ] }`

---

### Create Repository (Authenticated User)

```
POST /user/repos
```

**Request Body (CreateRepoOption):**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | **Yes** | Repository name |
| `description` | string | No | Repository description |
| `private` | boolean | No | Whether private (default: `false`) |
| `auto_init` | boolean | No | Initialize with README (default: `false`) |
| `default_branch` | string | No | Default branch name (default: server setting, usually `main`) |
| `gitignores` | string | No | Gitignore template(s), comma-separated (e.g., `"Go"`, `"Python,VisualStudioCode"`) |
| `issue_labels` | string | No | Issue label set name (e.g., `"Default"`) |
| `license` | string | No | License template (e.g., `"MIT"`, `"Apache-2.0"`) |
| `readme` | string | No | README template name (default: `"Default"`) |
| `template` | boolean | No | Make this a template repository |
| `trust_model` | string | No | Trust model: `"default"`, `"collaborator"`, `"committer"`, `"collaboratorcommitter"` |
| `object_format_name` | string | No | Object format: `"sha1"` or `"sha256"` |

```bash
# Create a basic public repo
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{repo}",
    "description": "{description}",
    "private": false,
    "auto_init": true
  }' \
  "${FORGEJO_URL}/api/v1/user/repos"

# Create private repo with license and gitignore
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{repo}",
    "description": "{description}",
    "private": true,
    "auto_init": true,
    "default_branch": "{branch}",
    "gitignores": "{gitignore_templates}",
    "license": "MIT",
    "readme": "Default"
  }' \
  "${FORGEJO_URL}/api/v1/user/repos"

# Create a template repo
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{repo}",
    "description": "{description}",
    "auto_init": true,
    "template": true,
    "gitignore": "{gitignore_template}",
    "license": "Apache-2.0"
  }' \
  "${FORGEJO_URL}/api/v1/user/repos"
```

**Query Parameters for `GET /user/repos`:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `order_by` | string | Order repos by field. Options include: `name`, `created`, `updated`, `pushed`, `size`, `id`, `stars`, `forks` |
| `page` | integer | Page number (1-based) |
| `limit` | integer | Page size |

```bash
# List your repos ordered by most recently updated
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/user/repos?order_by=updated&limit=20"
```

**Response:** `201 Created` — `{Repository}`

---

### Create Repository in Organization

```
POST /org/{org}/repos
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `org` | string | Organization name |

**Request Body:** Same as `POST /user/repos` (CreateRepoOption).

```bash
# Create repo in an organization
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{repo}",
    "description": "{description}",
    "private": true,
    "auto_init": true,
    "default_branch": "{branch}"
  }' \
  "${FORGEJO_URL}/api/v1/org/{org}/repos"
```

**Response:** `201 Created` — `{Repository}`

---

### Migrate Repository

```
POST /repos/migrate
```

**Request Body (MigrateRepoOptions):**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `clone_addr` | string | **Yes** | URL of the repository to migrate from |
| `repo_name` | string | **Yes** | Name for the new repository |
| `repo_owner` | string | No | Owner (user/org) of the new repo |
| `description` | string | No | Repository description |
| `auth_username` | string | No | Auth username for private source repos |
| `auth_password` | string | No | Auth password for private source repos |
| `auth_token` | string | No | Auth token for private source repos |
| `service` | string | No | Source service type: `"git"`, `"github"`, `"gitea"`, `"gitlab"`, `"gogs"`, `"onedev"`, `"codebase"`, `"gitbucket"` |
| `mirror` | boolean | No | Create as a mirror (stays synced with source) |
| `mirror_interval` | string | No | Mirror sync interval (e.g., `"8h30m0s"`) |
| `private` | boolean | No | Make repository private |
| `issues` | boolean | No | Migrate issues |
| `pull_requests` | boolean | No | Migrate pull requests |
| `releases` | boolean | No | Migrate releases |
| `milestones` | boolean | No | Migrate milestones |
| `labels` | boolean | No | Migrate labels |
| `wiki` | boolean | No | Migrate wiki |
| `lfs` | boolean | No | Migrate LFS objects |
| `lfs_endpoint` | string | No | LFS endpoint URL |

```bash
# Migrate a public GitHub repo
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://github.com/{owner}/{repo}.git",
    "repo_name": "migrated-repo",
    "service": "github",
    "mirror": false,
    "private": false,
    "description": "{description}"
  }' \
  "${FORGEJO_URL}/api/v1/repos/migrate"

# Migrate as mirror with all data
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://github.com/{owner}/{repo}.git",
    "repo_name": "project-mirror",
    "repo_owner": "{org}",
    "service": "github",
    "auth_token": "ghp_xxxxxxxxxxxx",
    "mirror": true,
    "mirror_interval": "8h",
    "wiki": true,
    "issues": true,
    "milestones": true,
    "labels": true,
    "pull_requests": true,
    "releases": true,
    "private": true,
    "description": "{description}"
  }' \
  "${FORGEJO_URL}/api/v1/repos/migrate"

# Migrate from GitLab with issues and PRs
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://gitlab.com/{owner}/{repo}.git",
    "repo_name": "from-gitlab",
    "service": "gitlab",
    "auth_token": "glpat-xxxxxxxxxxxx",
    "issues": true,
    "pull_requests": true,
    "milestones": true,
    "labels": true,
    "releases": true,
    "wiki": true,
    "private": true,
    "description": "{description}"
  }' \
  "${FORGEJO_URL}/api/v1/repos/migrate"

# Migrate a plain git repo (no metadata, just code)
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://{remote_host}/{owner}/{repo}.git",
    "repo_name": "external-repo",
    "service": "git",
    "auth_username": "user",
    "auth_password": "pass"
  }' \
  "${FORGEJO_URL}/api/v1/repos/migrate"
```

**Response:** `201 Created` — `{Repository}`

---

### Get Repository

```
GET /repos/{owner}/{repo}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `owner` | string | Repository owner (user or org) |
| `repo` | string | Repository name |

```bash
# Get repository details
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"
```

**Response:** `200 OK` — `{Repository}`

---

### Get Repository by ID

```
GET /repositories/{id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | integer | Numeric repository ID |

```bash
# Get repo by numeric ID
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repositories/{repo_id}"
```

**Response:** `200 OK` — `{Repository}`

---

### Edit Repository

```
PATCH /repos/{owner}/{repo}
```

See [settings.md](settings.md) for complete field documentation.

```bash
# Update description and website
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "{description}",
    "website": "https://example.com"
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"

# Make repo private
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "private": true
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"

# Change default branch
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "default_branch": "{branch}"
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"

# Archive a repository
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "archived": true
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"

# Configure merge settings
curl -s -X PATCH \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "allow_merge_commits": true,
    "allow_rebase": true,
    "allow_squash_merge": true,
    "allow_rebase_explicit": false,
    "default_merge_style": "squash",
    "default_delete_branch_after_merge": true
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"
```

**Response:** `200 OK` — `{Repository}`

---

### Delete Repository

```
DELETE /repos/{owner}/{repo}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `owner` | string | Repository owner |
| `repo` | string | Repository name |

```bash
# Delete a repository (IRREVERSIBLE)
curl -s -X DELETE \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}"
```

**Response:** `204 No Content`

---

### Create Repository from Template

```
POST /repos/{template_owner}/{template_repo}/generate
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `template_owner` | string | Template repo owner |
| `template_repo` | string | Template repo name |

**Request Body (GenerateRepoOption):**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `owner` | string | **Yes** | Owner of the new repo (user or org) |
| `name` | string | **Yes** | Name of the new repo |
| `description` | string | No | Description |
| `private` | boolean | No | Whether private |
| `git_content` | boolean | No | Copy git content (default: `true`) |
| `topics` | boolean | No | Copy topics |
| `git_hooks` | boolean | No | Copy git hooks |
| `webhooks` | boolean | No | Copy webhooks |
| `avatar` | boolean | No | Copy avatar |
| `labels` | boolean | No | Copy labels |
| `protected_branch` | boolean | No | Copy branch protections |
| `default_branch` | string | No | Default branch for new repo |

```bash
# Generate repo from template with all content
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "owner": "{owner}",
    "name": "{repo}",
    "description": "{description}",
    "private": false,
    "git_content": true,
    "topics": true,
    "labels": true,
    "protected_branch": true
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/generate"

# Generate into an organization
curl -s -X POST \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "owner": "{org}",
    "name": "{repo}",
    "private": true,
    "git_content": true,
    "topics": true,
    "webhooks": true,
    "labels": true
  }' \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/generate"
```

**Response:** `201 Created` — `{Repository}`

---

## Additional Utility Endpoints

### Get Raw File Content

```bash
# Get raw content of a file
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/raw/README.md"

# Get raw content from a specific branch
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/raw/src/main.go?ref={branch}"
```

### Download Archive

```bash
# Download as tar.gz
curl -s -L \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -o repo.tar.gz \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/archive/{branch}.tar.gz"

# Download as zip
curl -s -L \
  -H "Authorization: token ${FORGEJO_PAT}" \
  -o repo.zip \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/archive/{branch}.zip"
```

### Get Language Statistics

```bash
curl -s \
  -H "Authorization: token ${FORGEJO_PAT}" \
  "${FORGEJO_URL}/api/v1/repos/{owner}/{repo}/languages"
```

**Response:** `200 OK` — `{ "Go": 52345, "Python": 12000, ... }` (bytes per language)

---

## Related References

- [settings.md](settings.md) — Detailed EditRepoOption fields
- [collaborators.md](collaborators.md) — Collaborator and team management
- [topics.md](topics.md) — Topic management
- [forks.md](forks.md) — Fork operations
- [transfers.md](transfers.md) — Transfers and mirrors
- [stars-watchers.md](stars-watchers.md) — Stars and watch subscriptions
