Skip to content

Package Endpoints

These endpoints cover the Pub Spec v2 read surface (metadata and archives) plus club’s package-level administration, likes, scoring, and download stats.

Pub Spec v2

List All Versions of a Package

GET /api/packages/<package>
Accept: application/vnd.pub.v2+json

Primary endpoint used by dart pub get for dependency resolution.

Response: 200 OK

{
"name": "my_package",
"isDiscontinued": false,
"replacedBy": null,
"latest": {
"version": "2.1.0",
"retracted": false,
"archive_url": "https://club.example.com/api/archives/my_package-2.1.0.tar.gz",
"archive_sha256": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"pubspec": { "name": "my_package", "version": "2.1.0" },
"published": "2026-04-01T10:00:00.000Z"
},
"versions": [ "..." ]
}

Errors: 404 NotFound.


Inspect a Specific Version

GET /api/packages/<package>/versions/<version>

Returns metadata for a single version (same shape as an entry in the versions array above).

Errors: 404 NotFound.


Content (README / CHANGELOG / Example)

GET /api/packages/<package>/versions/<version>/content
GET /api/packages/<package>/content

Returns the rendered README, CHANGELOG, and example text for a version. The /<package>/content alias resolves to the package’s latest version.

{
"package": "my_package",
"version": "2.1.0",
"readme": "# my_package ...",
"changelog": "## 2.1.0 ...",
"example": "import ...",
"examplePath": "example/main.dart",
"binExecutables": [],
"screenshots": []
}

Download an Archive

GET /api/archives/<package>-<version>.tar.gz

Streams the .tar.gz tarball. Records a download stat. Response Content-Type: application/gzip.

Legacy URLs (/packages/<pkg>/versions/<v>.tar.gz and /api/packages/<pkg>/versions/<v>/archive.tar.gz) return 303 See Other redirects to the canonical archive URL.


Package Admin

All endpoints in this section require authentication as a package uploader, a member of the package’s publisher, or a server admin.

Get/Set Package Options

GET /api/packages/<package>/options
PUT /api/packages/<package>/options
{
"isDiscontinued": false,
"replacedBy": null,
"isUnlisted": false
}

PUT accepts any subset of the three fields. isUnlisted hides the package from search and listings.


Visibility

Controls whether a package can be read without credentials. See the Public Packages guide for the model.

GET /api/packages/<package>/visibility
{
"package": "my_pkg",
"visibility": "private",
"changedAt": null,
"changedBy": null,
"publicPackagesEnabled": true,
"permittedByEnvironment": true,
"canManage": true
}

permittedByEnvironment reports the PUBLIC_PACKAGES_ENABLED gate and publicPackagesEnabled the server admin toggle. canManage is true only when the caller may change this package and the feature is enabled, so a client can distinguish “not allowed” from “not turned on”.

POST /api/packages/<package>/visibility/preview

A dry run that changes nothing. Send the target visibility, optionally with the closure members currently selected:

{
"visibility": "public",
"closure": ["core_ui", "icons"]
}

The response reports the dependency closure that would move, the versions that would remain unresolvable, and any dependents the change would break. It is a POST rather than a GET because the request carries a set, not because it has side effects.

PUT /api/packages/<package>/visibility
{
"visibility": "public",
"confirm": "my_pkg",
"closure": ["core_ui", "icons"],
"acceptBreakage": false
}
FieldRequiredMeaning
visibilityyespublic or private. target is accepted as an alias
confirmyesMust echo the package name exactly, or the call is rejected
closurenoOmit to move the whole computed closure. An explicit [] flips only this package
acceptBreakagenoRequired to proceed when going private would break public dependents

Typed confirmation is deliberate friction: going public exposes the entire published history, and nothing already downloaded can be recalled.

Returns 403 when public packages are not enabled on the server, and 400 on a confirmation mismatch.


Get/Set Version Options (Retract)

GET /api/packages/<package>/versions/<version>/options
PUT /api/packages/<package>/versions/<version>/options
{ "isRetracted": true }

Uploaders

GET /api/packages/<package>/uploaders
PUT /api/packages/<package>/uploaders/<email>
DELETE /api/packages/<package>/uploaders/<email>

GET returns the current uploaders list. PUT and DELETE return { "status": "ok" }. The server refuses to remove the last uploader (400 InvalidInput).

{
"uploaders": [
{ "userId": "...", "email": "jane@example.com", "displayName": "Jane Doe" }
]
}

Errors: 404 NotFound (unknown email), 400 InvalidInput (removing the last uploader), 403 InsufficientPermissions (caller is not a package admin).


Publisher Ownership

GET /api/packages/<package>/publisher
PUT /api/packages/<package>/publisher
{ "publisherId": "acme" }

publisherId may be null when the package is individually owned. Setting a publisher clears individual uploaders.


Permissions Probe

GET /api/packages/<package>/permissions
Authorization: Bearer club_pat_...
{ "isAdmin": true }

Returns whether the caller can administer this package (as uploader, publisher admin, or server admin).


Activity Log

GET /api/packages/<package>/activity-log?before=<iso8601>

Returns up to 50 audit log entries for this package, newest first. Pass the createdAt of the oldest entry as before= to paginate. Requires authentication as a package admin (uploader, publisher admin, or server admin).

{
"entries": [
{
"id": "...",
"createdAt": "2026-04-01T10:00:00.000Z",
"kind": "package.version_published",
"summary": "Published my_package 2.1.0.",
"version": "2.1.0",
"agent": { "email": "jane@example.com", "displayName": "Jane Doe" }
}
]
}

The version and agent fields are present only when applicable.


Delete Package (package-owner)

DELETE /api/packages/<package>
Authorization: Bearer club_pat_...

Deletes the package and all versions. Allowed for package owners (uploaders or publisher admins). Admins should prefer /api/admin/packages/<package>.


Publishers

Publishers group packages under a shared owner.

Create a Publisher (admin)

POST /api/publishers
Authorization: Bearer club_pat_...
Content-Type: application/json
{
"id": "acme",
"displayName": "Acme Corp",
"description": "Packages by Acme Corp",
"websiteUrl": "https://acme.example.com",
"contactEmail": "packages@acme.example.com"
}

Get / Update a Publisher

GET /api/publishers/<id>
PUT /api/publishers/<id>

List Members

GET /api/publishers/<id>/members

Add / Update / Remove a Member

PUT /api/publishers/<id>/members/<userId>
DELETE /api/publishers/<id>/members/<userId>
{ "role": "admin" }

role is either member or admin.


Likes

Get Package Like Count

GET /api/packages/<package>/likes
{ "package": "my_package", "likes": 42 }

Like / Unlike (auth)

PUT /api/account/likes/<package>
DELETE /api/account/likes/<package>
{ "package": "my_package", "liked": true }

List Liked Packages

GET /api/account/likes
Authorization: Bearer club_pat_...

Scoring (pana)

Package Score

GET /api/packages/<package>/score

Returns the aggregate score for the package’s latest scored version.

Version Score

GET /api/packages/<package>/versions/<version>/score

Full Scoring Report

GET /api/packages/<package>/versions/<version>/scoring-report

Returns the pana report. The response status is one of:

StatusMeaning
disabledScoring is disabled for this server
not_analyzedNo score record exists yet for this version
pendingQueued for analysis
runningPana is currently scoring this version
completedReport available (includes grantedPoints, maxPoints, sections)
failedPana exited with an error

Downloads

GET /api/packages/<package>/downloads

Returns a rolling history of per-week download counts for a package.

{
"packageName": "my_package",
"total30Days": 540,
"weeks": [
{
"weekStart": "2025-04-14",
"weekLabel": "Apr 14",
"total": 120,
"byVersion": { "2.1.0": 100, "2.0.0": 20 }
}
]
}

Dartdoc

GET /api/packages/<package>/dartdoc-status

Returns generation status for the latest version’s dartdoc output.

GET /documentation/<package>/latest/*

Serves the generated dartdoc HTML for the latest version. Requests to any non-latest version redirect to /latest.