Download a package ZIP — a published version, or the draft
Download a specific version of a package as a ZIP file. Supports exact version, dist-tag, or semver range resolution. The literal draft downloads the author's working copy instead — the same tree GET /api/packages/{scope}/{name}/files?version=draft lists (the stored draft archive overlaid with the authoritative manifest.json and primary content from the database), zipped. Naming the draft is an author's act: it is reserved to callers who may WRITE the package (403 draft_not_writable otherwise, a system package included), on top of the visibility and <type>:read checks every download applies. restrict_package_copy does not apply to the draft: an author fetching their own working copy is editing it, as the file routes already let them, not copying it out. A draft archive has no integrity hash, so it carries no X-Integrity; it carries a strong ETag instead and answers If-None-Match with 304. Rate-limited to 50 requests/minute.
/api/packages/{scope}/{name}/{version}/downloadCookie session from Better Auth. Requires X-Org-Id header for org-scoped routes.
In: cookie
Path Parameters
Package scope (e.g. @myorg)
^@[a-z0-9][a-z0-9-]*$Package name
Exact version, dist-tag (e.g. 'latest'), semver range (e.g. '^1.0.0'), or the literal draft for the author's working copy.
Header Parameters
Organization ID. Required for cookie auth. Not needed for API key auth (org resolved from key).
uuidSpace ID. Required for space-scoped routes (agents, runs, schedules, and space-scoped module routes). Not needed for API key auth (space resolved from key).
Entity-tag of a cached draft archive (draft only). A match yields 304 Not Modified.
curl -X GET "https://your-instance/api/packages/string/string/string/download""string"{
"type": "https://docs.appstrate.dev/errors/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid or missing session",
"code": "unauthorized",
"request_id": "req_abc123"
}{
"type": "https://docs.appstrate.dev/errors/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "Insufficient permissions",
"code": "forbidden",
"request_id": "req_abc123"
}{
"type": "https://docs.appstrate.dev/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "Resource not found",
"code": "not_found",
"request_id": "req_abc123"
}{
"type": "https://docs.appstrate.dev/errors/package-archive-unreadable",
"title": "Package Archive Unreadable",
"status": 422,
"detail": "The package archive expands past the 50 MB decompression limit and was refused (decompressed-budget-exceeded). Republish the package from bytes that fit the limit.",
"code": "package_archive_unreadable",
"request_id": "req_abc123"
}{
"type": "https://docs.appstrate.dev/errors/rate-limited",
"title": "Rate Limited",
"status": 429,
"detail": "Too many requests. Please try again shortly.",
"code": "rate_limited",
"request_id": "req_abc123",
"retry_after": 30
}{
"type": "http://example.com",
"title": "string",
"status": 0,
"detail": "string",
"instance": "string",
"code": "string",
"request_id": "string",
"param": "string",
"retry_after": 0,
"errors": [
{
"field": "string",
"code": "string",
"message": "string",
"title": "string",
"candidate_connections": [
{
"id": "string",
"label": "string",
"account_id": "string",
"owned_by_actor": true,
"needs_reconnection": true
}
],
"connection_id": "string",
"missing_scopes": [
"string"
],
"owned_by_actor": true,
"required_scopes": [
"string"
],
"auth_key": "string",
"required_auth_key": "string",
"available_auth_keys": [
"string"
],
"connect_url": "http://example.com",
"expiresAt": "2019-08-24T14:15:22Z",
"packageId": "string"
}
]
}