---
title: "Stable v1 hosted download-plan alias."
method: GET
path: "/api/v1/artifacts/{id}/download-plan"
tags: ["Artifacts", "Downloads", "Agent Docs"]
---

# Stable v1 hosted download-plan alias.

`GET /api/v1/artifacts/{id}/download-plan`

## Request

- Base URL: `https://huggingbay.xyz`
- URL: `https://huggingbay.xyz/api/v1/artifacts/{id}/download-plan`
- Auth: none declared

recorded per-file download-plan. Returns hosted-only file rows (files[].path, sizeBytes, sha256, downloadUrl), the signed manifest URL and the serve verdict (downloadStatus, downloadable, reasonCodes). Agent flow (for one file straight from a model name, GET /api/downloadable-files?q=<model name> returns hosted-file inventory rows with filePath, sha256, downloadUrl and downloadStatus in one call; skip BLOCKED rows and null downloadUrl values): find {id} with GET /api/v1/search?q=<model name> (each row carries its id and serve state) or GET /api/v1/resolve?repo=<owner/name>; GET this plan (license: artifact.license), pick the row in files[] whose path names the file you want (the quant, e.g. Q4_K_M, is in the filename), GET its downloadUrl, then recompute SHA-256 locally (shasum -a 256 <file> or sha256sum <file>) and compare with files[].sha256. downloadStatus / downloadable are the only download authority; a URL appears only when downloadStatus is READY. A matching hash proves the bytes arrived unchanged, not that the file runs: serve.runReady and serve.commandsAvailable stay false until a tested run plan is recorded (reason code no-tested-run-plan), and a GGUF file needs a local runner such as llama.cpp, Ollama or LM Studio. Per-file run.commands, when present, are runner guidance; each command's note says whether that line was checked.

**Inputs.** Path, query and header fields are listed below. No request body is required.

**Outputs.** See each status below for its media type, schema and headers. A missing record or unavailable capability is not a successful empty result.

**Errors.** API JSON failures use the documented response schema. Read code, message and next; retry only when the response allows it. Auth, invalid input and missing records need a caller change.

**Rate limits.** Public read budgets apply by client IP and route group. This operation can have a smaller route-specific budget. Read Retry-After on 429 or retryable 503 responses.

**Example setup.** Use an identifier returned by the matching list, search or create operation. Sample identifiers are not a promise that a record exists.

## Path parameters

- `id` string, required

## Query parameters

- `tool` 'ollama' | 'lm-studio' | 'comfyui' | 'transformers' | 'llama.cpp'
- `snapshot` string

## Response `200`

Hosted-file download-plan

- object: One hosted download-plan. Obtainability has exactly one authority: `serve.downloadReady` (machine) and `serveExplanation.downloadStatus` (READY / BLOCKED, with `reason` in plain words). Per-file `url`/`downloadUrl` is present only when that authority says READY. The top-level `status` is the STORAGE state and is deprecated for availability decisions.
  - `schemaVersion` 'hugging-bay.artifact-download-plan.v1', required
  - `generatedAt` string, date-time
  - `artifactId` string, required: The one id key every surface uses (same as artifact.id).
  - `artifact` object, required
    - `id` string, required
    - `repo` string, nullable
    - `type` string, nullable
    - `license` string, nullable
    - `visibility` string
    - `hostingStatus` string: Projection of the live serve decision: hosted | partial | external | unknown.
    - `upstreamUrl` string, nullable
  - `status` string: DEPRECATED for availability: the recorded STORAGE state (e.g. complete-bytes = bytes are stored) — NOT an obtainability, runnability or verification claim. It stays complete-bytes on a withheld artifact. Read `serve.downloadReady` / `serveExplanation.downloadStatus` instead.
  - `serve` object, required: The single non-contradictory obtain contract (hugging-bay.serve-contract.nv5). Agrees with every per-file affordance below.
    - `schemaVersion` string
    - `decisionVersion` string
    - `evaluatedAt` string, date-time
    - `downloadReady` boolean, required: TRUE only when at least one hosted file is served right now.
    - `runReady` boolean
    - `bytesStatus` string
    - `byteVerificationStatus` string: Stored-mirror byte verification; separate from localHashAttested (client claim) and serverTransferVerified (one proxied transfer).
    - `commandsAvailable` boolean
    - `reasonCodes` string[], required: Why downloadReady is false (empty when READY), e.g. manifest-not-final, unverified-bytes, no-strictly-downloadable-files, quarantined.
  - `downloadStatus` 'READY' | 'BLOCKED' | 'PENDING' | 'UNKNOWN': Top-level obtain verdict, same evaluation as serveExplanation.downloadStatus.
  - `serveExplanation` object, required: The plain-words twin of `serve`, from the same evaluation (same serveEvaluationId).
    - `downloadable` boolean, required
    - `downloadStatus` 'READY' | 'BLOCKED' | 'PENDING' | 'UNKNOWN', required
    - `reason` string, required
    - `reasonCodes` string[]
    - `serveEvaluatedAt` string, date-time
    - `serveEvaluationId` string
  - `serveEvaluatedAt` string, date-time
  - `serveEvaluationId` string
  - `files` object[], required: Hosted file rows. A row is LISTED for inventory even when withheld; only a row with a non-false `url`/`downloadUrl` is downloadable now.
    - `path` string, required
    - `sizeBytes` integer, required
    - `sha256` string, required: The full published SHA-256 of the exact bytes; verify locally with `sha256sum <file>` (older Mac: `shasum -a 256 <file>`).
    - `url` union: Legacy alias of downloadUrl; false/null when withheld.
      - string
      - boolean
    - `downloadUrl` union: recorded byte-download URL. Responds HTTP 302 to signed storage; follow redirects.
      - string
      - boolean
    - `downloadUrlResponse` object
      - `status` unknown, required
      - `followRedirects` unknown, required
    - `verifiedDownloadUrl` string, nullable: Server-stream-verified lane, when available.
    - `completionAttestationUrl` string, nullable: POST here after a full download + local SHA-256 match, with the X-Hugging-Bay-Completion-Token from the redirect.
    - `contentType` string, nullable
    - `role` string, nullable
  - `stats` object: Counts over `files`: `files` = rows downloadable now, `listedFiles` = rows listed (including withheld), `withheldFiles` = listed minus downloadable.
    - `files` integer
    - `listedFiles` integer
    - `withheldFiles` integer
    - `bytes` integer
    - `externalFiles` integer, nullable
    - `allArtifactFiles` integer, nullable
  - `manifest` object
  - `completionAttestation` object
  - `fileIntegrity` object
  - `verify` object
  - `commands` object[]

## Other responses

- `400`: Invalid request or query parameter.
- `403`: Gated terms required
- `409`: External-only artifact is not mirrored
- `423`: Manifest is not final yet
- `default`: Request failed; legacy extra fields are preserved.

## Changes

- **2026-10-20** `ea7ba26b1e27`: 1 info
  - added the new optional `query` request parameter `snapshot`

[Operation history](https://skmtc.dev/huggingbay/apis/hugging-bay-public-api/changes/api/v1/artifacts/:id/download-plan/get.md)

---

[API](https://skmtc.dev/huggingbay/apis/hugging-bay-public-api.md) · [All operations](https://skmtc.dev/huggingbay/apis/hugging-bay-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/huggingbay/apis/hugging-bay-public-api/revisions/ea7ba26b1e27?raw)
