# HTTP API

The public, read-only HTTP endpoints for the catalog, model files, listings and read-only project links, with parameters, responses and examples.

Source: https://rack-maker.cbnsndwch.dev/docs/api

## Before you start

> **Unversioned:**
>
> These endpoints are what the Rack Maker app itself uses. They are not versioned and may change without notice. Don't
> build anything that can't cope with a field being added, renamed or removed.

- The endpoints on this page are read-only, need no account, and are safe to call from a script or `curl`.
- Every other endpoint (saving projects, drafting and publishing entries, uploading files, reporting, signing in, syncing between browsers, moderation) is for the app itself. They need a signed-in browser session and aren't documented here.
- **Using an AI agent?** Connect it to the MCP server instead of calling this API. The MCP server signs your agent in as you and gives it tools made for the job. See [Agents](https://rack-maker.cbnsndwch.dev/docs/agents).

All paths are relative to `https://rack-maker.cbnsndwch.dev`.

### Responses and errors

Answers are JSON (`content-type: application/json`), unless noted. An error has an HTTP status and a body like this:

```json
{ "error": "no such entry" }
```

Some errors add a `detail` field. Unknown paths answer `404` with `{ "error": "not found" }`.

### Caching and limits

JSON answers are sent with `cache-control: no-store`: the server doesn't cache them, and nor should anything between you and it. If you poll, cache on your side and keep it gentle, say once every few minutes. The only cacheable answers are files (model files and link-preview images), noted below.

There is no search endpoint. The catalog page searches in your browser: to search, fetch the [snapshot](#get-apicatalogsnapshot) or the [entry list](#get-apicatalogentries) and filter it yourself.

## Catalog

Catalog ids are `<namespace>/<slug>`: the namespace is lowercase letters, digits and dashes; the slug may also contain dots. In paths, the namespace and slug are two segments. See [Namespaces](https://rack-maker.cbnsndwch.dev/docs/catalog/namespaces).

Catalog data is CC BY-SA 4.0: if you republish it, credit its authors and share alike. See [Licences and credits](https://rack-maker.cbnsndwch.dev/docs/catalog/licences-and-credits).

### GET /api/catalog/entries

One summary per entry, sorted by id.

```bash
curl https://rack-maker.cbnsndwch.dev/api/catalog/entries
```

```json
[
    {
        "id": "cbnsndwch/shelf-10in",
        "name": "10″ shelf",
        "kind": "part",
        "category": "shelf",
        "latestPublished": 1,
        "hidden": false
    }
]
```

| Field                            | Meaning                                                                                         |
| -------------------------------- | ----------------------------------------------------------------------------------------------- |
| `id`, `name`, `kind`, `category` | the entry; `kind` is `part` or `assembly`                                                       |
| `latestPublished`                | the newest published revision number, or `null` if nothing is published yet                     |
| `hidden`                         | `true` when a moderator (or enough reports) has hidden it; see [Reports](https://rack-maker.cbnsndwch.dev/docs/catalog/reports) |

The list includes every entry, including ones with no published revision and hidden ones. Filter on `latestPublished` and `hidden` for what the catalog page shows.

### GET /api/catalog/published

When each entry's newest published revision was published, and by whom: their handle (`by`), display name (`name`) and user id (`user`, the id entries' credits use). Hidden entries are left out. Newest first.

```bash
curl https://rack-maker.cbnsndwch.dev/api/catalog/published
```

```json
[
    {
        "id": "alice/mini-pc-tray",
        "rev": 2,
        "at": "2026-09-20T14:03:11.000Z",
        "by": "alice",
        "name": "Alice Chen",
        "user": "github:1234567"
    }
]
```

Entries that ship with the app show `system` as `by`. `at`, `by`, `name` and `user` can be `null`.

### GET /api/catalog/snapshot

Every published revision of every entry, with its full definition. This is what the catalog loads, and the easiest way to get everything at once.

```bash
curl https://rack-maker.cbnsndwch.dev/api/catalog/snapshot
```

```json
{
    "entries": [{ "id": "cbnsndwch/shelf-10in", "rev": 1, "kind": "part", "name": "10″ shelf", "…": "…" }],
    "statuses": { "cbnsndwch/shelf-10in@1": "published" },
    "hidden": ["bob/spam-part"]
}
```

| Field      | Meaning                                                                                                                                   |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `entries`  | full entry definitions, one per revision (older revisions included), sorted by id and then revision                                       |
| `statuses` | each revision's status, keyed `<id>@<rev>`; without an account, always `published`                                                        |
| `hidden`   | ids of hidden entries. Their revisions are still in `entries` (projects that use them must still resolve), but don't offer them to people |

The fields of an entry are described in [Authoring parts](https://rack-maker.cbnsndwch.dev/docs/catalog/authoring#entry-reference). To show "the" revision of each entry, take the highest published `rev` per id and skip hidden ids.

### GET /api/catalog/entries/:namespace/:slug

The entry's revisions, newest first, without their definitions.

```bash
curl https://rack-maker.cbnsndwch.dev/api/catalog/entries/cbnsndwch/shelf-10in
```

```json
{
    "id": "cbnsndwch/shelf-10in",
    "canDraft": false,
    "revisions": [
        {
            "id": "cbnsndwch/shelf-10in",
            "rev": 1,
            "status": "published",
            "sha256": "9f2c…",
            "authorId": "…",
            "forkedFrom": null,
            "createdAt": "2026-09-01T00:00:00.000Z",
            "updatedAt": "2026-09-01T00:00:00.000Z",
            "submittedAt": null,
            "publishedAt": "2026-09-01T00:00:00.000Z",
            "can": { "change": false, "review": false }
        }
    ]
}
```

| Field                    | Meaning                                                                     |
| ------------------------ | --------------------------------------------------------------------------- |
| `revisions[].status`     | `review`, `published` or `rejected` (drafts are only shown to their author) |
| `revisions[].sha256`     | a hash of the revision's definition                                         |
| `revisions[].forkedFrom` | `<id>@<rev>` it was copied from, if any                                     |
| `revisions[].authorId`   | an opaque account id, not a handle                                          |
| `canDraft`, `can`        | what the caller may do; always `false` without an account                   |

Answers `404` when there is no such entry.

### GET /api/catalog/entries/:namespace/:slug/:rev

One revision, with its full definition in `entry`. The other fields are those of a revision in the list above, without `can`.

```bash
curl https://rack-maker.cbnsndwch.dev/api/catalog/entries/cbnsndwch/shelf-10in/1
```

```json
{
    "id": "cbnsndwch/shelf-10in",
    "rev": 1,
    "status": "published",
    "publishedAt": "2026-09-01T00:00:00.000Z",
    "…": "…",
    "entry": { "id": "cbnsndwch/shelf-10in", "rev": 1, "kind": "part", "name": "10″ shelf", "…": "…" }
}
```

Without an account, only published revisions can be read. Anything else, or a `rev` that isn't a whole number from 1 up, answers `404`.

## Model files

Model files are the GLB and STL files catalog entries are drawn from. Each keeps its own licence: see [Licences and credits](https://rack-maker.cbnsndwch.dev/docs/catalog/licences-and-credits). An entry that uses one has `"geometry": { "kind": "mesh", "asset": { "asset": "<id>" } }` in its definition, where the id is the file's SHA-256.

The files that ship with the app aren't in this API. Their ids start with `bundled/`, and they are served as static files from `/models/`, for example `/models/rack-19in-12u.stl`. Their credits are on the [Credits](https://rack-maker.cbnsndwch.dev/credits) page.

### GET /api/assets

Without parameters: every model file that hasn't been taken down, newest first, up to 500. With `ids`: those files, taken down or not.

| Parameter | Meaning                                           |
| --------- | ------------------------------------------------- |
| `ids`     | optional: up to 200 file ids, separated by commas |

```bash
curl "https://rack-maker.cbnsndwch.dev/api/assets?ids=3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b"
```

```json
[
    {
        "id": "3a7bd3e2…",
        "kind": "mesh-glb",
        "license": "CC-BY-4.0",
        "bytes": 1843200,
        "source": {
            "origin": "third-party",
            "provider": "Sketchfab",
            "title": "Some switch",
            "author": "someone",
            "authorUrl": "https://…",
            "url": "https://…",
            "changes": "Scaled to fit the part's envelope.",
            "acknowledgements": "…",
            "retrievedAt": "2026-09-20T14:03:11.000Z"
        },
        "meta": { "format": "glb", "…": "…" },
        "uploader": "alice",
        "createdAt": "2026-09-20T14:03:11.000Z",
        "hidden": false
    }
]
```

| Field           | Meaning                                                                                                      |
| --------------- | ------------------------------------------------------------------------------------------------------------ |
| `kind`          | `mesh-glb` or `mesh-stl`                                                                                     |
| `license`       | an SPDX-style id: `CC0-1.0`, `CC-BY-4.0`, `CC-BY-SA-4.0`, `CC-BY-3.0`, `CC-BY-SA-3.0`, `MIT` or `Apache-2.0` |
| `source.origin` | `own` (the uploader made it) or `third-party`                                                                |
| `source`        | where it comes from and whom to credit; optional fields may be `null`                                        |
| `meta`          | what the server read from the file when it was uploaded                                                      |
| `uploader`      | the uploader's handle                                                                                        |
| `hidden`        | `true` when it was taken down after a report                                                                 |

### GET /api/assets/:id

One file's details, as a single object shaped like an item of the list above. `404` when there is no such file.

```bash
curl https://rack-maker.cbnsndwch.dev/api/assets/3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b
```

### GET /api/assets/:id/file

The file itself, as `model/gltf-binary` (GLB) or `model/stl` (STL).

```bash
curl -o model.glb https://rack-maker.cbnsndwch.dev/api/assets/3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b/file
```

- Cached publicly for a day (`cache-control: public, max-age=86400`). A file never changes under the same id, but it can be taken down.
- `410` when it was taken down after a report; `404` when there is no such file.
- Up to 15 MB.

Before you share the file, check its licence and credits with `GET /api/assets/:id`.

## Listings

### GET /api/listings

Where to get catalog entries: online shops and physical stores, each with its latest price and stock report. Hidden listings are left out.

| Parameter | Meaning                                                                           |
| --------- | --------------------------------------------------------------------------------- |
| `ids`     | catalog entry ids, separated by commas, up to 200. Without it, the answer is `{}` |

```bash
curl "https://rack-maker.cbnsndwch.dev/api/listings?ids=cbnsndwch/shelf-10in,cbnsndwch/cage-nut-m6"
```

```json
{
    "cbnsndwch/shelf-10in": [
        {
            "id": "V1StGXR8_Z5jdHi6B-myT",
            "catalogId": "cbnsndwch/shelf-10in",
            "vendor": "Some shop",
            "url": "https://…",
            "channel": "online",
            "location": null,
            "sku": null,
            "region": null,
            "kind": "new",
            "packQty": 1,
            "price": { "amount": 24.9, "currency": "EUR", "at": "2026-09-20T14:03:11.000Z" },
            "stock": { "status": "in-stock", "at": "2026-09-20T14:03:11.000Z" }
        }
    ]
}
```

| Field     | Meaning                                                                                                    |
| --------- | ---------------------------------------------------------------------------------------------------------- |
| (keys)    | the catalog ids that have listings; ids without any are left out                                           |
| `channel` | `online` (with its product page in `url`) or `store` (a physical store at `location`; `url` may be `null`) |
| `kind`    | `new`, `used`, `print-service` or `self-print`                                                             |
| `packQty` | items per purchase                                                                                         |
| `price`   | the latest price with its three-letter currency and date, or `null`                                        |
| `stock`   | the latest stock report (`in-stock`, `low` or `out-of-stock`) with its date, or `null`                     |

Prices and stock are reported by contributors and dated. The app treats a price older than 90 days as stale. See [BOM & quote](https://rack-maker.cbnsndwch.dev/docs/editor/bom-and-quote).

## Read-only project links

A project's owner can share a read-only link, `/v/<token>`, where the token is 21 characters of letters, digits, `_` and `-`. See [Sharing](https://rack-maker.cbnsndwch.dev/docs/editor/sharing). These endpoints read what such a link shows. They never reveal the project's own id, which is what grants editing.

### GET /api/view/:token

The project behind a read-only link, as it was last saved.

```bash
curl https://rack-maker.cbnsndwch.dev/api/view/Uakgb_J5m9g-0JDMbcJqL
```

```json
{
    "project": {
        "id": "",
        "name": "Office rack",
        "createdAt": "2026-09-10T09:00:00.000Z",
        "updatedAt": "2026-09-20T14:03:11.000Z",
        "claimed": true,
        "mine": false
    },
    "state": {
        "id": 0,
        "version": 1790000000000,
        "updatedAt": "2026-09-20T14:03:11.000Z",
        "doc": { "…": "…" }
    }
}
```

| Field                    | Meaning                                                                         |
| ------------------------ | ------------------------------------------------------------------------------- |
| `project.id`, `state.id` | always empty or 0 here                                                          |
| `project.name`           | may be `null`                                                                   |
| `state`                  | `null` when nothing has been saved yet                                          |
| `state.version`          | changes on every save; compare it to see whether the project changed            |
| `state.doc`              | the project: its racks and parts, each pointing at a catalog entry and revision |

`404` with `{ "error": "no such link" }` when the token matches no project.

### GET /api/view/:token/og.jpg

The link-preview picture of a read-only link: a 1200 × 630 JPEG of the project in 3D. The owner's browser makes it while they have the project open, so a project may not have one yet. `HEAD` works too.

| Parameter | Meaning                                                                     |
| --------- | --------------------------------------------------------------------------- |
| `v`       | optional: the picture's version, as it appears in the page's `og:image` tag |

```bash
curl -o preview.jpg https://rack-maker.cbnsndwch.dev/api/view/Uakgb_J5m9g-0JDMbcJqL/og.jpg
```

- With the current `v`, the answer is cached for a year (`immutable`): a new picture gets a new version. Without it, or with an old one, it is cached for 5 minutes.
- It carries an `ETag`.
- `404` when there is no such link or no picture yet. Link previews then use the site's default picture.

The easiest way to get the current URL, with its version, is the `og:image` tag in the HTML of `/v/<token>`.
