API Reference
OpenRepo REST API Reference
This page documents the OpenRepo REST API as used by openrepo-sync. The API is built on Django REST Framework and serves both the OpenRepo web frontend and CLI clients.
Source: github.com/openkilt/openrepo — verified from
web/repo/api/urls.py,views.py,serializers.py, andcli/openrepo_cli/rest_interface.py.
Base URL
All endpoints are prefixed with /api/ (applied by nginx or the top-level Django URL config — not present in individual URL patterns).
https://your-openrepo-host/api/
Authentication
Every request must include a token in the Authorization header:
Authorization: Token <api_key>
- Authentication backend:
rest_framework.authentication.TokenAuthentication - Obtain your API key from
GET /api/whoami→api_keyfield, or from the OpenRepo web UI user profile
Example
curl -H "Authorization: Token abc123" https://openrepo.example.com/api/whoami
Permissions
The global permission class is CustomOpenRepoPermission:
| Request type | Who can call it |
|---|---|
| Safe methods (GET, HEAD, OPTIONS) | Any authenticated user |
| Write methods (POST, PUT, DELETE) | Superusers only — except the four viewsets below |
Pass-through viewsets (non-superusers may write, subject to object-level checks):
UploadViewSetCopyViewSetRepoViewSetPackageViewSet
Response Format
- Success: JSON body, or
{}for operations with no return value - Error: JSON body with error detail, matching the HTTP status code
- 401 Unauthorized: invalid or missing token
Endpoints
GET /api/whoami
Returns details of the currently authenticated user. No trailing slash.
Response fields (UserDetailSerializer):
| Field | Type | Description |
|---|---|---|
href |
URL | Self-link |
username |
string | Username of the authenticated user |
is_superuser |
boolean | Whether the user has superuser permissions |
email |
string | User email address |
api_key |
string | The API token (read-only, source='auth_token') |
Example:
curl -H "Authorization: Token abc123" https://openrepo.example.com/api/whoami
{
"href": "https://openrepo.example.com/api/users/1/",
"username": "alice",
"is_superuser": false,
"email": "alice@example.com",
"api_key": "abc123..."
}
Router-Registered Resources
The following resources are registered with DRF’s DefaultRouter, generating standard list and detail endpoints:
| Prefix | Resource | Standard endpoints |
|---|---|---|
/api/users/ |
Users | GET list, GET/PUT/PATCH/DELETE detail |
/api/repos/ |
Repositories | GET list, GET/PUT/PATCH/DELETE detail |
/api/signingkeys/ |
PGP signing keys | GET list, GET/PUT/PATCH/DELETE detail |
/api/builds/ |
Builds | GET list, GET/PUT/PATCH/DELETE detail |
/api/buildlogs/ |
Build logs | GET list, GET/PUT/PATCH/DELETE detail |
Note:
PackageViewSetexists but is commented out of the router — packages are accessed via the hand-coded URL patterns below.
GET /api/<repo_uid>/
Retrieve repository detail.
Response fields (RepoDetailSerializer):
| Field | Type | Description |
|---|---|---|
repo_uid |
string | Unique repository identifier |
repo_type |
string | Package type (e.g. deb, rpm) |
package_count |
integer | Number of packages in the repo |
href_upload |
URL | Pre-built upload URL for this repo (hypermedia) |
href_packages |
URL | Link to the packages listing for this repo |
signing_key |
string | PGP signing key UID, if configured |
keep_only_latest |
boolean | Whether the repo auto-prunes to one version |
last_updated |
datetime | Timestamp of last package change |
promote_to |
string | Target repo UID for promotion, if configured |
repo_instructions |
string | Human-readable install instructions |
write_access |
boolean | Whether the authenticated user can write |
POST /api/<repo_uid>/upload/
Upload a package to the repository.
Request: multipart/form-data
| Field | Required | Description |
|---|---|---|
package_file |
Yes | Binary package file. Field name must be exactly package_file |
overwrite |
No | Accepts "1", "true", or "yes" (case-insensitive) to overwrite an existing package |
Server behaviour:
- Computes a SHA-512 checksum of the uploaded file
- Deduplicates: if an identical file (by checksum) already exists in another repo, the file is shared on disk rather than stored twice
Example:
curl -X POST \
-H "Authorization: Token abc123" \
-F "package_file=@mypkg_1.2.3_amd64.deb" \
https://openrepo.example.com/api/my-repo/upload/
With overwrite:
curl -X POST \
-H "Authorization: Token abc123" \
-F "package_file=@mypkg_1.2.3_amd64.deb" \
-F "overwrite=1" \
https://openrepo.example.com/api/my-repo/upload/
GET /api/repos/<repo_uid>/packages/
List all packages in a repository. Response is paginated.
Response format:
{
"count": 42,
"next": "https://openrepo.example.com/api/repos/my-repo/packages/?page=2",
"previous": null,
"results": [...]
}
results item fields (PackageSummarySerializer):
| Field | Type |
|---|---|
href_package |
URL |
package_uid |
string |
package_name |
string |
filename |
string |
architecture |
string |
upload_date |
datetime |
version |
string |
checksum_sha512andbuild_dateare absent from list responses. Use the detail endpoint to retrieve them.
GET /api/<repo_uid>/pkg/<package_uid>/
Retrieve details of a specific package.
Response fields (PackageDetailSerializer — superset of list fields):
| Field | Type | Notes |
|---|---|---|
package_uid |
string | |
repo_uid |
string | Read-only, sourced from repo relation |
filename |
string | |
version |
string | |
architecture |
string | |
checksum_sha512 |
string | Not present in list responses |
build_date |
datetime | Not present in list responses |
upload_date |
datetime |
PUT /api/<repo_uid>/pkg/<package_uid>/
Update package metadata.
DELETE /api/<repo_uid>/pkg/<package_uid>/
Delete a package from the repository. Uses MultipleFieldLookupMixin with lookup_fields = ('repo__repo_uid', 'package_uid').
Example:
curl -X DELETE \
-H "Authorization: Token abc123" \
https://openrepo.example.com/api/my-repo/pkg/abc-def-123/
POST /api/<repo_uid>/pkg/<package_uid>/copy/
Copy a package to another repository. The exact request body fields (destination repo identifier) were not confirmed in source analysis — inspect CopyViewSet directly for the current field name.
Endpoints Used by openrepo-sync
| Method | Path | Purpose |
|---|---|---|
GET |
/api/whoami |
Verify credentials on startup |
GET |
/api/repos/<repo_uid>/packages/ |
List packages in a repo (paginated) |
POST |
/api/<repo_uid>/upload/ |
Upload a new package |
DELETE |
/api/<repo_uid>/pkg/<package_uid>/ |
Delete an old package (pruning) |
Pagination
List endpoints follow DRF’s standard pagination format:
{
"count": 42,
"next": "https://openrepo.example.com/api/repos/my-repo/packages/?page=2",
"previous": null,
"results": [...]
}
openrepo-sync follows the next URL automatically until all pages are fetched.
Error Responses
| Status | Meaning |
|---|---|
401 Unauthorized |
Missing or invalid API token |
403 Forbidden |
Valid token but insufficient permissions |
404 Not Found |
Repository or package does not exist |
400 Bad Request |
Invalid request (e.g. missing package_file field) |
500 Internal Server Error |
Server-side error |
Note: Exact HTTP status codes for successful operations (upload, delete) were not confirmed from server source analysis. Do not assume 201/204 — test against a live instance.
Open Questions
The following API details were not confirmed from source analysis and may require testing against a live instance:
- Exact request body field for
POST /api/<repo_uid>/pkg/<package_uid>/copy/(destination repo identifier) - HTTP status codes for successful upload and delete
- Pagination class (
PageNumberPaginationvsCursorPagination) and defaultpage_size - Upload response body schema (does it return the new
package_uid?) BuildViewSet/BuildLogViewSetfield shapes and whether a build trigger endpoint exists