<!-- Source: https://docs.biosimulant.com/references/cli/registry-api -->

# Registry API v1

Registry API v1 is the headless protocol used by the Biosimulant CLI for Hub and compatible third-party registries. Biosimulant Hub is the default implementation.

The examples below use `https://hub.biosimulant.com` as the registry origin. Clients must discover the API base instead of assuming its path.

## Discovery

Request the well-known document without authentication:

```http
GET /.well-known/biosimulant-registry HTTP/1.1
Host: hub.biosimulant.com
```

The response identifies the protocol version, API base, authentication endpoints, and capabilities:

```json
{
  "protocolVersion": "1",
  "apiBase": "/api/registry/v1",
  "auth": {
    "type": "bearer",
    "tokenEndpoint": "/api/registry/v1/auth/exchange",
    "workspaceExchangeTokenEndpoint": "/api/registry/v1/auth/workspace-exchange-tokens"
  },
  "capabilities": [
    "search",
    "metadata",
    "versions",
    "pull",
    "publish",
    "private-packages",
    "workspace-token-exchange",
    "immutable-versions",
    "checksum-idempotency"
  ]
}
```

Resolve relative URLs against the registry origin. A client should reject an unsupported `protocolVersion` or a registry that does not advertise a capability required by the requested operation.

## Package endpoints

All paths in this table are relative to the discovered `apiBase`.

| Method | Path | Purpose | Required access |
| --- | --- | --- | --- |
| `GET` | `/packages?q=&type=&page=&pageSize=` | Search visible packages | Anonymous for public packages; `packages:read` for private packages |
| `GET` | `/packages/{namespace}/{name}` | Resolve package metadata and its default version | Same as search |
| `GET` | `/packages/{namespace}/{name}/versions` | List immutable versions | Same as search |
| `GET` | `/packages/{namespace}/{name}/{version}` | Resolve one exact version and checksum | Same as search |
| `GET` | `/packages/{namespace}/{name}/{version}/download` | Download the exact artifact | Same as search |
| `POST` | `/packages` | Publish an artifact | `packages:write` |

Search accepts `type=model` or `type=lab`, uses one-based pages, and limits `pageSize` to 100. Package and version responses include the canonical reference, SHA-256 digest, byte size, visibility, status, and download URL.

Private packages intentionally return `404` when the caller cannot access them. This avoids revealing whether an inaccessible package exists.

## Authentication

Send an operation token as a bearer credential:

```http
Authorization: Bearer <operation-token>
```

Public reads may be anonymous. Private reads require `packages:read`; publishing requires `packages:write`.

Long-lived user credentials must not be copied into Studio workspaces. An authenticated application can issue a workspace-bound exchange token:

```http
POST /auth/workspace-exchange-tokens
Content-Type: application/json

{
  "workspaceSessionId": "16a74428-370d-4557-a92f-92dce233505f",
  "scopes": ["packages:read", "packages:write"]
}
```

The workspace exchanges that credential when an operation begins:

```http
POST /auth/exchange
Authorization: Bearer <workspace-exchange-token>
Content-Type: application/json

{
  "registry": "https://hub.biosimulant.com",
  "scopes": ["packages:read"]
}
```

The result contains a short-lived `registry_operation` bearer token, its scopes, audience, and expiry. Requested scopes cannot exceed those granted to the workspace token, and the workspace session must remain active.

## Publishing

Publish the package archive as the request body:

```http
POST /packages
Authorization: Bearer <operation-token>
Content-Type: application/gzip
X-Biosimulant-Filename: cell-study-1.0.0.tar.gz
X-Biosimulant-Sha256: 68b0…
X-Biosimulant-Package: acme/cell-study
X-Biosimulant-Version: 1.0.0
X-Biosimulant-Visibility: private

<artifact bytes>
```

`X-Biosimulant-Filename` and the complete lowercase hexadecimal `X-Biosimulant-Sha256` are required. Package, version, and visibility headers make the caller's intent explicit; the server verifies them against the artifact manifest.

Publishing a new version returns `201` with:

```json
{
  "artifact": {
    "reference": "acme/cell-study@1.0.0",
    "version": "1.0.0",
    "sha256": "68b0…"
  },
  "idempotent": false
}
```

Repeating the same package version with the same checksum is idempotent and returns `200` with `idempotent: true`. Reusing that immutable version with different content returns `409 immutable_version_conflict`.

## Errors and verification

Registry errors use the HTTP status plus a JSON `detail` value. `detail` can be a message or a structured object with a stable `code` and supporting fields.

| Status | Meaning |
| --- | --- |
| `401` | Missing, expired, or invalid credential |
| `403` | Authenticated principal lacks the requested scope |
| `404` | Package, version, or active workspace session was not found or is not visible |
| `409` | The immutable version already exists with a different checksum |
| `422` | Invalid manifest, checksum, scope, reference, version, or request header |

Clients must calculate the downloaded artifact's SHA-256 and compare it with the resolved version before extraction. A checksum mismatch is a hard failure and must never be cached as a successful pull.

## Compatibility requirements

A compatible Registry API v1 implementation must:

- expose the well-known discovery document;
- return immutable, checksum-addressed versions;
- support anonymous public reads;
- enforce `packages:read` and `packages:write` where authentication is required;
- make identical publishes idempotent;
- reject same-version, different-content publishes with `409`;
- avoid putting credentials in references, redirect URLs, or logs.

See [Registry references and authentication](/references/cli/registry-auth) for CLI usage and credential storage.
