Skip to content

Admin API ​

50 endpoints. Request and response schemas are described on the schemas page; the other tags are listed in the API index.

Endpoints ​

GET /api/v1/admin/audit ​

List audit entries

List audit log entries, newest first (admin only). Two paging modes: offset paging (page/per_page, the default) and keyset paging (cursor). Keyset paging never counts rows or scans past an offset, so it stays fast on large tables; follow next_cursor while has_more is true. In offset mode total is exact when total_exact is true (small tables and the last page); otherwise it is the planner's row estimate (no filter) or a lower bound (with actor/action filters).

Parameters

NameInTypeRequiredDescription
pagequeryintegerno1-based page number (offset paging). Ignored when cursor is present. Minimum: 1.
per_pagequeryintegernoMinimum: 1. Maximum: 100.
cursorquerystringnoOpaque next_cursor of the previous page. Present but empty starts keyset paging at the newest entry. Selects keyset paging: the response carries no page, total or total_exact.
actorquerystringnoCase-insensitive substring of the actor name or actor id.
actionquerystringnoCase-insensitive substring of the action name.

Responses

StatusDescriptionBody
200Paginated list of audit entriesapplication/json: object
400Malformed cursor-
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
dataarray of AuditEntryno-
has_morebooleannoTrue when entries follow this page.
next_cursorstringnoPass as cursor to fetch the next page. Absent when has_more is false.
pageintegernoOffset paging only.
per_pageintegerno-
totalintegernoOffset paging only. See total_exact.
total_exactbooleannoOffset paging only. False when total is an estimate or a lower bound.

GET /api/v1/admin/audit/export ​

Export audit log

Export the audit log as JSONL or CSV (admin only). Streams the log as a download without counting rows: entries are read in keyset pages (newest first) and flushed after every page, so memory stays bounded however long the log is. The body is chunked (no Content-Length); if the server fails after the response started the connection is aborted rather than ended normally, so a truncated file is detectable. In CSV, cells starting with =, +, -, @, tab or CR are prefixed with ' to defuse spreadsheet formula injection. Fields (stable; new fields may be appended): id, timestamp (RFC 3339, UTC), action, actor, actor_type, actor_id, resource_type, resource_id, repository_id, project_id, ip, details (JSON object; in CSV its JSON text). Unset values are null in JSONL and empty in CSV.

Parameters

NameInTypeRequiredDescription
formatquerystringnojsonl (default): one JSON object per line. csv: header row plus one record per entry. One of: jsonl, csv. Default: jsonl.
sincequerystringnoOnly entries at or after this instant: RFC 3339 timestamp or YYYY-MM-DD (00:00 UTC).
untilquerystringnoOnly entries strictly before this instant (same forms as since); must be after since.
actorquerystringnoCase-insensitive substring of the actor name or actor id.
actionquerystringnoCase-insensitive substring of the action name.

Responses

StatusDescriptionBody
200Audit export stream Headers: Content-Disposition.application/x-ndjson: string; text/csv: string
400Unknown format, malformed since/until, or since not before until-
403Admin access required-

GET /api/v1/admin/backup ​

List backups

List all backups (admin only)

Responses

StatusDescriptionBody
200List of backupsapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
backupsarray of Backupno-

POST /api/v1/admin/backup ​

Create backup

Trigger a new backup (admin only)

Request body (required)

application/json: object

FieldTypeRequiredDescription
typestringyesOne of: full, metadata-only.

Responses

StatusDescriptionBody
202Backup startedapplication/json: object
400Invalid requestapplication/json: Error
403Admin access required-

Fields of the 202 response (application/json):

FieldTypeRequiredDescription
created_atstring (date-time)no-
idstring (uuid)no-
statusstringnoOne of: pending, in_progress, completed, failed.
typestringnoOne of: full, metadata-only.

GET /api/v1/admin/backup/{id} ​

Get backup

Get details of a specific backup (admin only)

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes-

Responses

StatusDescriptionBody
200Backup detailsapplication/json: Backup
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404Backup not found-

GET /api/v1/admin/backup/{id}/download ​

Download backup

Download a backup file (admin only)

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes-

Responses

StatusDescriptionBody
200Backup fileapplication/octet-stream: string (binary)
400Invalid requestapplication/json: Error
403Forbiddenapplication/json: Error
404Backup not found-

GET /api/v1/admin/gpg/keys ​

List GPG keys

Lists the database-managed signing keys plus the read-only entry for the key configured through gpg.keyPath / GPG_KEY_PATH (admin only). Private key material is never returned.

Responses

StatusDescriptionBody
200List of GPG keysapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
can_store_keysbooleannoFalse when OPENARTIFACT_SECRET_KEY is not set: keys cannot be imported or generated because private keys are only stored encrypted.
keysarray of GPGKeyno-

POST /api/v1/admin/gpg/keys ​

Import GPG key

Imports an ASCII-armored private key (admin only). A passphrase-protected key is unlocked with passphrase and stored without it, sealed with OPENARTIFACT_SECRET_KEY. The key becomes the global active key when nothing signs globally yet, unless activate says otherwise.

Request body (required)

application/json: object

FieldTypeRequiredDescription
activatebooleannoMake it the global active key; default: only when nothing signs globally yet
namestringnoUnique display name; defaults to key-<keyid>. Maximum length: 255.
passphrasestringnoPassphrase of a protected key
private_keystringyesASCII-armored private key

Responses

StatusDescriptionBody
201GPG key importedapplication/json: GPGKey
400Invalid, protected (code passphrase_required), revoked or expired key-
403Admin access required-
409Name or fingerprint already exists, or OPENARTIFACT_SECRET_KEY is not set (code secret_key_required)-

POST /api/v1/admin/gpg/keys/generate ​

Generate GPG key

Generates a signing key pair on the server (admin only). The private key is stored sealed and never returned; export the public key with the export route.

Request body

application/json: object

FieldTypeRequiredDescription
activatebooleannoMake it the global active key; default: only when nothing signs globally yet
algorithmstringnoOne of: ed25519, rsa4096. Default: ed25519.
commentstringno-
emailstringno-
namestringnoMaximum length: 255.
user_namestringnoUser id name; default OpenArtifact repository signing key

Responses

StatusDescriptionBody
201GPG key generatedapplication/json: GPGKey
400Invalid input-
403Admin access required-
409Name already exists, or OPENARTIFACT_SECRET_KEY is not set (code secret_key_required)-

DELETE /api/v1/admin/gpg/keys/{id} ​

Delete GPG key

Deletes a database-managed key (admin only). An active key (global or assigned to a repository) is refused with 409 key_in_use unless force=true; the repositories it signed are then re-signed by the next applicable key or become unsigned. The config key cannot be deleted.

Parameters

NameInTypeRequiredDescription
idpathstringyesKey id (UUID) or config for the read-only file/env key
forcequerybooleanno-

Responses

StatusDescriptionBody
204GPG key deleted-
400Invalid requestapplication/json: Error
403Admin access required-
404GPG key not found-
409Key is active (key_in_use) or read-only-

PUT /api/v1/admin/gpg/keys/{id}/activate ​

Activate GPG key

Makes the key sign every deb, rpm and pacman repository without a key of its own (global scope), or only the named hosted deb, rpm or pacman repository when repository is given. Affected repositories are re-signed (admin only).

Parameters

NameInTypeRequiredDescription
idpathstringyesKey id (UUID) or config for the read-only file/env key

Request body

application/json: object

FieldTypeRequiredDescription
repositorystringnoRepository name; omit for the global scope

Responses

StatusDescriptionBody
200The keyapplication/json: GPGKey
400Repository is not a hosted deb, rpm or pacman repository-
403Admin access required-
404Key or repository not found-
409Read-only config key-

DELETE /api/v1/admin/gpg/keys/{id}/activate ​

Deactivate GPG key

Stops the key signing globally, or for the repository named in the repository query parameter (admin only).

Parameters

NameInTypeRequiredDescription
idpathstringyesKey id (UUID) or config for the read-only file/env key
repositoryquerystringno-

Responses

StatusDescriptionBody
204Deactivated-
400Invalid requestapplication/json: Error
403Admin access required-
404Key or repository not found-
409The key was not active there, or it is the read-only config key-

GET /api/v1/admin/gpg/keys/{id}/export ​

Export public key

Downloads the ASCII-armored public key (admin only).

Parameters

NameInTypeRequiredDescription
idpathstringyesKey id (UUID) or config for the read-only file/env key

Responses

StatusDescriptionBody
200Armored public keyapplication/pgp-keys: string
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Admin access required-
404GPG key not found-

GET /api/v1/admin/license/policies ​

List all license policies

List all license policies (admin only)

Responses

StatusDescriptionBody
200List of license policies-
403Forbiddenapplication/json: Error

POST /api/v1/admin/license/policies ​

Create global license policy

Create a global license policy (admin only)

Request body (required)

application/json: LicensePolicy

Responses

StatusDescriptionBody
201License policy created-
400Invalid requestapplication/json: Error
403Forbiddenapplication/json: Error

GET /api/v1/admin/quotas ​

List all quotas

Every live repository and every project with its quota and live usage, largest usage first, in one request. A repository or project without a limit is listed as unlimited. Admin only.

Parameters

NameInTypeRequiredDescription
scopequerystringnoOne of: repository, project.
limitedquerybooleannotrue keeps only quotas that enforce a limit.
qquerystringnoCase-insensitive substring of the repository or project name.
pagequeryintegernoDefault: 1. Minimum: 1.
limitqueryintegernoDefault: 500. Minimum: 1. Maximum: 2000.

Responses

StatusDescriptionBody
200One page of quotasapplication/json: QuotaOverviewList
400Invalid scope-
403Admin access required-

GET /api/v1/admin/recovery/status ​

Disaster-recovery journal status

State of the row journal behind openartifact rebuild: how many changed package rows still wait for their sidecar blob, how old the oldest is, the last failure and the progress of the backfill reconcile (admin only)

Responses

StatusDescriptionBody
200Journal statusapplication/json: RecoveryStatus
403Admin access required-

POST /api/v1/admin/restore ​

Restore backup

Restore from a backup file (admin only) - multipart form upload

Request body (required)

multipart/form-data: object

FieldTypeRequiredDescription
filestring (binary)yesBackup file to restore

Responses

StatusDescriptionBody
200Restore started-
400Invalid backup file-
409A restore is already in progress-

GET /api/v1/admin/sso-providers ​

List SSO providers

List all SSO providers (admin only)

Responses

StatusDescriptionBody
200List of SSO providersapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
providersarray of SSOProvideryes-

POST /api/v1/admin/sso-providers ​

Create SSO provider

Create a new SSO provider (admin only)

Request body (required)

application/json: object

FieldTypeRequiredDescription
configobjectno-
display_namestringyes-
enabledbooleanno-
kindstringyesOne of: oidc, saml.
slugstringyes-

Responses

StatusDescriptionBody
201SSO provider createdapplication/json: SSOProvider
400Invalid requestapplication/json: Error
403Admin access required-

GET /api/v1/admin/sso-providers/{slug} ​

Get SSO provider

Get details of a specific SSO provider (admin only)

Parameters

NameInTypeRequiredDescription
slugpathstringyes-

Responses

StatusDescriptionBody
200SSO provider detailsapplication/json: SSOProvider
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404SSO provider not found-

PATCH /api/v1/admin/sso-providers/{slug} ​

Update SSO provider

Update an SSO provider (admin only)

Parameters

NameInTypeRequiredDescription
slugpathstringyes-

Request body (required)

application/json: object

FieldTypeRequiredDescription
configobjectno-
display_namestringno-
enabledbooleanno-

Responses

StatusDescriptionBody
200SSO provider updatedapplication/json: SSOProvider
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404SSO provider not found-

DELETE /api/v1/admin/sso-providers/{slug} ​

Delete SSO provider

Delete an SSO provider (admin only)

Parameters

NameInTypeRequiredDescription
slugpathstringyes-

Responses

StatusDescriptionBody
204SSO provider deleted-
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404SSO provider not found-

POST /api/v1/admin/sso-providers/{slug}/test ​

Test SSO provider

Test an SSO provider configuration (admin only)

Parameters

NameInTypeRequiredDescription
slugpathstringyes-

Responses

StatusDescriptionBody
200Test resultapplication/json: object
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
404SSO provider not found-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
errorstringno-
kindstringno-
messagestringno-
okbooleanno-

GET /api/v1/admin/storage ​

List storage backends

List the storage backends stored in the database (items) and the primary store the server is configured with (primary, read-only; absent when none is wired). The primary store is not part of items or total. Its status comes from a health check cached for 10 seconds.

Responses

StatusDescriptionBody
200List of storage backendsapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
itemsarray of StorageBackendno-
primaryStoragePrimaryno-
totalintegerno-

POST /api/v1/admin/storage ​

Create storage backend

Create a new storage backend (admin only). The names "primary" and "replication" are reserved (they are static paths under /admin/storage): creating one answers 400

Request body (required)

application/json: object

FieldTypeRequiredDescription
configobjectno-
kindstringyesOne of: filesystem, s3, gcs, azure.
namestringyes-

Responses

StatusDescriptionBody
201Storage backend createdapplication/json: StorageBackend
400Invalid requestapplication/json: Error
403Admin access required-

GET /api/v1/admin/storage/primary ​

Get the primary storage

The primary blob store the server is configured with (storage.* settings), with its non-secret location, live status and usage (admin only). The name "primary" is reserved: this static path takes precedence over /admin/storage/{name}.

Responses

StatusDescriptionBody
200The primary storageapplication/json: StoragePrimary
403Admin access required-
404No primary storage is wired-

PATCH /api/v1/admin/storage/primary ​

Update the primary storage (not allowed)

Always answers 405 read_only: the primary storage is set by the server configuration.

Responses

StatusDescriptionBody
403Admin access required-
405read_onlyapplication/json: Error

DELETE /api/v1/admin/storage/primary ​

Delete the primary storage (not allowed)

Always answers 405 read_only: the primary storage is set by the server configuration.

Responses

StatusDescriptionBody
403Admin access required-
405read_onlyapplication/json: Error

POST /api/v1/admin/storage/primary/test ​

Test the primary storage

Runs a live health check through the store that serves every request and refreshes the cached status (admin only).

Responses

StatusDescriptionBody
200Test resultapplication/json: object
403Admin access required-
404No primary storage is wired-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
errorstringno-
messagestringno-
okbooleanno-

GET /api/v1/admin/storage/replication ​

Blob replication status

Backlog, lag, totals and the last verify pass of the replication to storage.replica (admin only). Reports enabled=false when no replica is configured. This static path takes precedence over /admin/storage/{name}.

Responses

StatusDescriptionBody
200Replication statusapplication/json: ReplicationStatus
403Admin access required-

POST /api/v1/admin/storage/replication/verify ​

Verify the replica now

Requests a verify pass that compares the primary store with the replica and repairs differences in either direction; it runs in the background on the replica holding the verify lock (admin only)

Responses

StatusDescriptionBody
202Pass requestedapplication/json: object
403Admin access required-
404Blob replication is not configured-
409A verify pass is already running or requested-

Fields of the 202 response (application/json):

FieldTypeRequiredDescription
requestedbooleanno-

GET /api/v1/admin/storage/{name} ​

Get storage backend

Get details of a specific storage backend (admin only)

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Responses

StatusDescriptionBody
200Storage backend detailsapplication/json: StorageBackend
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404Storage backend not found-

PATCH /api/v1/admin/storage/{name} ​

Update storage backend

Update a storage backend (admin only). Renaming it to "primary" or "replication" answers 400. The name "primary" answers 405 read_only unless a record of that name still exists from before it was reserved, which this then renames. Changing kind or config is refused with 409 while the backend holds blobs, serves a tier or is named by a tier move

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Request body (required)

application/json: object

FieldTypeRequiredDescription
configobjectno-

Responses

StatusDescriptionBody
200Storage backend updatedapplication/json: StorageBackend
400Invalid requestapplication/json: Error
403Forbiddenapplication/json: Error
404Storage backend not found-
405The name is the reserved primary storage, which is set by the server configuration and read-only (error code read_only)application/json: Error
409The kind or config cannot change while the backend holds blobs, serves a tier or is named by a tier move-

DELETE /api/v1/admin/storage/{name} ​

Delete storage backend

Delete a storage backend (admin only)

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Responses

StatusDescriptionBody
204Storage backend deleted-
400Invalid requestapplication/json: Error
403Forbiddenapplication/json: Error
404Storage backend not found-
405The name is the reserved primary storage, which is set by the server configuration and read-only (error code read_only)application/json: Error

POST /api/v1/admin/storage/{name}/test ​

Test storage backend

Test connection to a storage backend (admin only)

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Responses

StatusDescriptionBody
200Test resultapplication/json: object
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
404Storage backend not found-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
errorstringno-
messagestringno-
okbooleanno-

POST /api/v1/admin/tiering/dry-run ​

Simulate storage policies

Reports what a mover pass would move now (counts, bytes, per route and policy, and a sample) without changing anything. With policy set only that draft is simulated; otherwise every enabled policy and promote-on-read are (admin only)

Request body (required)

application/json: object

FieldTypeRequiredDescription
policyStoragePolicyInputno-
sample_limitintegernoBetween 0 and 200; default 200.

Responses

StatusDescriptionBody
200Simulation resultapplication/json: TierDryRun
400Invalid request-
403Admin access required-

GET /api/v1/admin/tiering/policies ​

List storage policies

Lists lifecycle policies in evaluation order (admin only)

Responses

StatusDescriptionBody
200List of policiesapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
itemsarray of StoragePolicyno-
totalintegerno-

POST /api/v1/admin/tiering/policies ​

Create storage policy

Creates a lifecycle policy; the first enabled policy matching a blob decides its tier (admin only)

Request body (required)

application/json: StoragePolicyInput

Responses

StatusDescriptionBody
201Policy createdapplication/json: StoragePolicy
400Invalid request-
403Admin access required-
409Name already used-

GET /api/v1/admin/tiering/policies/{id} ​

Get storage policy

Gets a lifecycle policy (admin only)

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes-

Responses

StatusDescriptionBody
200Policyapplication/json: StoragePolicy
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Admin access required-
404Storage policy not found-

PUT /api/v1/admin/tiering/policies/{id} ​

Replace storage policy

Replaces every field of a lifecycle policy (admin only)

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes-

Request body (required)

application/json: StoragePolicyInput

Responses

StatusDescriptionBody
200Policy updatedapplication/json: StoragePolicy
400Invalid request-
403Admin access required-
404Storage policy not found-
409Name already used-

DELETE /api/v1/admin/tiering/policies/{id} ​

Delete storage policy

Deletes a lifecycle policy; blobs already moved stay where they are (admin only)

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes-

Responses

StatusDescriptionBody
204Policy deleted-
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Admin access required-
404Storage policy not found-

POST /api/v1/admin/tiering/run ​

Run the tier mover now

Queues an immediate mover pass on the replica holding the mover lock (admin only)

Responses

StatusDescriptionBody
202Pass requestedapplication/json: object
403Admin access required-
409The mover is disabled-

Fields of the 202 response (application/json):

FieldTypeRequiredDescription
startedbooleannoFalse when a pass was already running.

GET /api/v1/admin/tiering/status ​

Tier mover status

State of the background mover and the moves still in its crash-recovery journal (admin only)

Responses

StatusDescriptionBody
200Mover statusapplication/json: TierMoverStatus
403Admin access required-

GET /api/v1/admin/tiering/tiers ​

List storage tiers

Lists the storage tiers (hottest first) with their placement: blob count and bytes per tier (admin only)

Responses

StatusDescriptionBody
200List of tiersapplication/json: object
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
itemsarray of StorageTierno-
totalintegerno-

POST /api/v1/admin/tiering/tiers ​

Create storage tier

Maps an existing storage backend to a named tier (admin only)

Request body (required)

application/json: object

FieldTypeRequiredDescription
backendstringyesName of an existing storage backend.
namestringyes-
promote_on_readbooleanno-
rankintegernoPosition from hot (low) to cold (high); defaults to after the last tier.

Responses

StatusDescriptionBody
201Tier createdapplication/json: StorageTier
400Invalid request-
403Admin access required-
409Name or backend already used by a tier-

PATCH /api/v1/admin/tiering/tiers/{name} ​

Update storage tier

Renames a tier or changes its rank or promote-on-read flag (admin only)

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Request body (required)

application/json: object

FieldTypeRequiredDescription
namestringno-
promote_on_readbooleanno-
rankintegerno-

Responses

StatusDescriptionBody
200Tier updatedapplication/json: StorageTier
400Invalid request-
403Admin access required-
404Storage tier not found-
409Name already used-

DELETE /api/v1/admin/tiering/tiers/{name} ​

Delete storage tier

Deletes a tier that holds no blobs and is not referenced by a policy; the primary tier cannot be deleted (admin only)

Parameters

NameInTypeRequiredDescription
namepathstringyes-

Responses

StatusDescriptionBody
204Tier deleted-
400The primary tier cannot be deleted-
403Admin access required-
404Storage tier not found-
409Tier still holds blobs, has moves in progress or is used by a policy-

GET /api/v1/admin/tokens ​

List all API tokens

List all API tokens across users (admin only)

Parameters

NameInTypeRequiredDescription
pagequeryintegernoMinimum: 1.
per_pagequeryintegernoMinimum: 1. Maximum: 100.

Responses

StatusDescriptionBody
200Paginated list of tokensapplication/json: object
400Invalid requestapplication/json: Error
403Admin access required-

Fields of the 200 response (application/json):

FieldTypeRequiredDescription
dataarray of AdminTokenno-
pageintegerno-
per_pageintegerno-
totalintegerno-

DELETE /api/v1/admin/tokens/{tokenId} ​

Revoke any API token

Revoke any user's API token (admin only)

Parameters

NameInTypeRequiredDescription
tokenIdpathstring (uuid)yes-

Responses

StatusDescriptionBody
204Token revoked-
400Invalid request, including a NUL byte in the URL or the bodyapplication/json: Error
403Forbiddenapplication/json: Error
404Token not found-

Free software under the AGPL-3.0-or-later.