# Workbenches and registry

Every organization Workbench is internal until a reviewed publish makes it public. An internal Workbench resolves, downloads, and lists only for a key of its organization holding `catalog:read`, or a signed-in member. Everyone else gets 404, the same as for a Workbench that does not exist.

## The Workbench object

`GET /v1/workbenches/:id`, `POST /v1/resolutions`, and `POST /v1/searches` return Workbench objects:

| Field                                           | Type                                              | Meaning                                                                                                                   |
| ----------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `id`                                            | UUID                                              | Use with `GET /v1/workbenches/:id`.                                                                                       |
| `workbench_id`                                  | UUID                                              |                                                                                                                           |
| `slug`                                          | string                                            | The registry name.                                                                                                        |
| `name`                                          | string                                            |                                                                                                                           |
| `summary`                                       | string or null                                    |                                                                                                                           |
| `runner`                                        | string                                            |                                                                                                                           |
| `model`                                         | string                                            | The model ID.                                                                                                             |
| `spec`                                          | `0` or `1`                                        | The manifest spec of the latest version.                                                                                  |
| `runtimes`                                      | list of strings                                   | Declared runtimes, in declaration order.                                                                                  |
| `runtime`                                       | string                                            | The first of `runtimes`, kept for older clients.                                                                          |
| `requirements`                                  | object                                            | `os`, `arch`, `cpu`, `memory_gb`, `disk_gb`, and `gpu` (default `false`).                                                 |
| `sandboxed`                                     | boolean                                           | True when `runtimes` includes `docker` or `e2b` with an image, or `daytona`. Only sandboxed Workbenches run in the cloud. |
| `status`                                        | `active`, `unlisted`, `deprecated`, or `rejected` |                                                                                                                           |
| `visibility`                                    | `public` or `private`                             | `private` is internal.                                                                                                    |
| `source`                                        | `github` or `registry`                            | Where the package came from.                                                                                              |
| `source_path`, `source_reference`, `source_url` | strings                                           | Provenance.                                                                                                               |
| `publisher`                                     | object                                            | `id`, `slug`, `aliases`, `name`, `accent`, `logo_url`, `verified`.                                                        |
| `repository`                                    | object or null                                    | `id`, `owner`, `name`, `url`.                                                                                             |
| `latest_version`                                | object                                            | See below.                                                                                                                |
| `metrics`                                       | object                                            | `saves`, `runs`.                                                                                                          |
| `character_roll`                                | integer                                           | Which character the registry draws for it.                                                                                |
| `first_published_at`, `updated_at`              | date-time                                         |                                                                                                                           |

`latest_version` has `id`, `version`, `spec`, `source_commit`, `digest` (SHA-256 of the package), `artifact_url`, `manifest` (the parsed manifest, with `model` as `{ id, routes? }`), `artifact_size`, `validation` (`valid`, `validator`), `published_at`, and `provenance`: `human_reviewed`, `trusted_publisher`, `internal` (stored by the organization, not reviewed), or `legacy`.

## List Workbenches

`GET /v1/workbenches`

Open. Public Workbenches, plus your own organization's internal ones when the caller can see them. Response `200` `{ "workbenches": [ ... ] }`.

## Get a Workbench

`GET /v1/workbenches/:id`

Open. One Workbench with its latest version.

## Search

`POST /v1/searches`

Open. With `organization_id`, a key with `catalog:read` or a signed-in member also gets that organization's internal Workbenches.

| Field             | Type                         | Meaning                                                                                   |
| ----------------- | ---------------------------- | ----------------------------------------------------------------------------------------- |
| `query`           | string, up to 120 characters | Free text.                                                                                |
| `publisher_slug`  | string                       | Only this publisher.                                                                      |
| `slug`            | string                       | Only this name.                                                                           |
| `organization_id` | UUID                         | Include this organization's internal Workbenches.                                         |
| `runnable`        | `any` or `cloud`             | `any`, the default, returns every visible Workbench. `cloud` returns only sandboxed ones. |

Response `200` `{ "workbenches": [ ... ] }`.

## Resolve a name

`POST /v1/resolutions`

Open. Resolves `publisher` and `workbench` names to one Workbench.

```json
{ "publisher": "acme", "workbench": "code-review" }
```

Response `200`: the Workbench. 404 when it does not exist or you cannot see it.

## Package files

`GET /v1/artifacts/:id`

Open, for a version the caller can see. `:id` is a version ID. Returns the package:

```json
{
    "format": 1,
    "files": [
        { "path": "workbench.yml", "content": "<base64>", "executable": false }
    ]
}
```

A package holds at most 256 files. Each `path` is up to 512 characters and each `content` is base64.

## Push a version

`POST /v1/versions`

Key `packages:write`, or session: manager. Body limit 16 MB.

| Field             | Type    | Meaning                                                  |
| ----------------- | ------- | -------------------------------------------------------- |
| `organization_id` | UUID    | Required.                                                |
| `slug`            | string  | The registry name. Defaults to the manifest's `name`.    |
| `package`         | package | Required. `{ "format": 1, "files": [ ... ] }`, as above. |

Stores the package as a new immutable internal version. It never makes a Workbench public. The Workbench is created as internal when it does not exist.

Response `201`:

```json
{
    "workbench": {
        "id": "uuid",
        "slug": "code-review",
        "organization_slug": "acme",
        "visibility": "private"
    },
    "version": { "id": "uuid", "version": "1.2.0", "digest": "..." }
}
```

| Status           | When                                                                                                                                                        |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400              | The manifest is invalid. A mix of spec 0 and spec 1 fields names the fields; another `spec` value is `Manifest spec N is not supported`.                    |
| 402 `plan_limit` | A new Workbench would pass the plan's private Workbench cap.                                                                                                |
| 409              | The manifest version is not greater than every stored version, or the Workbench is public. A public Workbench takes new versions only through a submission. |

Pushing the same version with identical bytes again changes nothing. `wb push` and the MCP `push` tool call this route.

## Submissions

A submission asks for public review. Approval makes the Workbench public. There is no other path to public.

### Submit for review

`POST /v1/submissions`

Body limit 16 MB. The body is one of three forms:

| Form             | Body                                                                                                                                                       | Auth                                                            |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| A stored version | `{ "version_id": "uuid", "files_become_public": false }`                                                                                                   | Key `packages:write` with manager creator, or session: manager. |
| A package        | `{ "organization_id": "uuid", "slug": "code-review", "package": { ... } }`                                                                                 | Key `packages:write`, or session: member.                       |
| A GitHub source  | `{ "repository_id": "uuid", "source_commit": "<40 to 64 characters>", "source_paths": [".workbenches/core/workbench.yml"], "files_become_public": false }` | Key `packages:write`, or session: member.                       |

Only the latest stored version can be submitted. `files_become_public` acknowledges that files from a private repository become publicly downloadable after approval. Response `201` `{ "submissions": [ ... ], "next_cursor": null }`.

### The submission object

| Field                                                    | Meaning                                                                                         |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `id`                                                     |                                                                                                 |
| `organization_id`, `publisher_slug`, `publisher_name`    | The publisher.                                                                                  |
| `submitted_by`                                           | The user.                                                                                       |
| `slug`, `name`, `version`, `digest`                      | What was submitted.                                                                             |
| `status`                                                 | `pending`, `changes_requested`, `approved`, `rejected`, `withdrawn`, or `superseded`.           |
| `kind`                                                   | `new` or `update`.                                                                              |
| `source`                                                 | `registry` or `github`, with `repository_id`, `source_commit`, `source_path`, `repository_url`. |
| `manifest`, `artifact_size`                              | The package.                                                                                    |
| `visibility`, `files_become_public`                      |                                                                                                 |
| `feedback`                                               | The reviewer's feedback, or null.                                                               |
| `superseded_by`, `version_id`, `latest_approved_version` |                                                                                                 |
| `dashboard_url`                                          | Where to follow it.                                                                             |
| `created_at`, `updated_at`, `decided_at`                 |                                                                                                 |

Reviewers approve an exact digest, request changes with feedback, or reject with feedback. Changed bytes for the same unapproved version supersede the earlier submission. If a newer version is pushed while a review is open, approval fails and the newer version must be submitted.

### List submissions

`GET /v1/submissions`

Key `packages:write` (its own organization only), or a session (your own submissions).

| Query             | Type              | Meaning                               |
| ----------------- | ----------------- | ------------------------------------- |
| `organization_id` | UUID              |                                       |
| `status`          | submission status |                                       |
| `cursor`          | UUID              | `next_cursor` from the previous page. |
| `limit`           | integer, 1 to 200 | Default 100.                          |

Response `200` `{ "submissions": [ ... ], "next_cursor": "uuid or null" }`.

### Get, withdraw, and read a submission

| Method | Path                          | Auth                                     | Result                                                                                                                                     |
| ------ | ----------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/v1/submissions/:id`         | Key `packages:write`, or session: member | `{ "submission", "history", "previous" }`. `history` is the audit trail: `id`, `actor_id`, `action`, `subject_id`, `reason`, `created_at`. |
| DELETE | `/v1/submissions/:id`         | Key `packages:write`, or session: member | Withdraw an open submission. Same response.                                                                                                |
| GET    | `/v1/submissions/:id/package` | Key `packages:write`, or session: member | The submitted package files.                                                                                                               |

## Unpublish

`DELETE /v1/publications/:id`

Key `packages:write` with manager creator, or session: manager. `:id` is the Workbench ID. Returns a public Workbench to internal at once and withdraws its open submissions. Copies already downloaded stay with their clients.

Response `200`:

```json
{
    "unpublished": true,
    "workbench": {
        "id": "uuid",
        "slug": "code-review",
        "organization_slug": "acme",
        "visibility": "private"
    }
}
```

`POST /v1/publications`, the legacy direct publish, returns 409 while review is required. Use `POST /v1/submissions`.

## Change the character

`PATCH /v1/workbenches/:id` with `{ "character_roll": 0 }` (0 to 1,000,000)

Key `packages:write` with manager creator, or session: manager. Changes which character the registry draws for the Workbench. Returns the Workbench.

## CLI counts

`POST /v1/events`

Open. The CLI records an anonymous `save` or `run` of a registry version here. Replies `202` `{ "accepted": true }`.

| Field             | Type                       |
| ----------------- | -------------------------- |
| `idempotency_key` | UUID                       |
| `version_id`      | UUID                       |
| `kind`            | `save` or `run`            |
| `cli_version`     | string, 1 to 40 characters |
| `occurred_at`     | date-time                  |
