---
title: 'REST API: Catalog'
path: reference/rest-api/catalog
status: published
---

# REST API: Catalog

Per-tenant private flow packages. List, publish, install, change visibility, delete.

All endpoints under `/api/v1/catalog`. All require ScaiKey OIDC (the dev-token path doesn't reach the catalog).

## `GET /v1/catalog`

List packages visible to the caller.

Visibility:

- `tenant`-scoped packages — anyone with `scaicore:view` in the tenant sees them.
- `groups`-scoped packages — only members of one of the `visibility_group_ids` see them.
- The author of a package always sees their own packages regardless of visibility.
- Super admins see all packages across all tenants.

**Response:**

```jsonc
[
  {
    "package_id": "customer-support",
    "name": "Customer Support",
    "description": "Triage + KB + HITL",
    "version": "0.2.0",
    "author_id": "usr_alice",
    "author_email": "alice@acme.example",
    "visibility": "tenant",
    "visibility_group_ids": [],
    "created_at": "2026-04-01...",
    "updated_at": "2026-04-29..."
  }
]
```

`scaicore:view`.

## `POST /v1/catalog/publish`

Publish a flow as a new catalog package (or bump the version of an existing one).

**Body — by flow id (preferred; the server fetches the latest content):**

```jsonc
{
  "package_id": "customer-support",
  "name": "Customer Support",
  "description": "Triage + KB + HITL",
  "version": "0.2.0",
  "readme": "# Customer Support\n\n...",
  "flow_id": "flow_abc123"
}
```

**Body — by inline content (for unsaved flows):**

```jsonc
{
  "package_id": "customer-support",
  "name": "Customer Support",
  "version": "0.2.0",
  "content": { /* full FlowGraph */ }
}
```

**Behavior:**

1. Compiles the flow to YAML server-side. If compile fails, returns 400 with the compile error — no half-published package.
2. Bundles `flow.json` + `manifest.yaml` + `package.json` + optional `README.md` into a deterministic `.scaipkg`.
3. Writes to object storage at `catalog/{tenant_id}/{package_id}/v{version}.scaipkg`.
4. Upserts the `CatalogPackage` row pointing at the latest version.

If you republish with the **same** `version` string, the patch number auto-bumps (`0.1.0` → `0.1.1`). To pin a deliberate version, provide a new value.

**Response:** `201 Created` with the `CatalogPackageSummary`.

`scaicore:manage` + `deploy` ACL on the source flow (if `flow_id` provided).

## `GET /v1/catalog/{package_id}/install`

Install a package — returns the flow JSON with a fresh `id` and `"Copy of {name}"` label, ready for the caller to save as their own flow.

**Response:**

```jsonc
{
  "flow": { /* full FlowGraph with fresh id + "Copy of..." name */ },
  "metadata": {
    "package_id": "customer-support",
    "name": "Customer Support",
    "version": "0.2.0",
    "author_email": "alice@acme.example"
  }
}
```

The returned flow has a freshly minted `id`, so saving it creates a new flow row rather than overwriting the package author's original. Other content (nodes, edges, config) is verbatim.

`scaicore:view` + visibility check.

## `PATCH /v1/catalog/{package_id}/visibility`

Change a package's visibility.

**Body:**

```jsonc
{
  "visibility": "groups",
  "visibility_group_ids": ["group_acme_eng", "group_acme_ops"]
}
```

Only the author or a tenant admin / super admin can change visibility.

**Response:** updated `CatalogPackageSummary`.

## `DELETE /v1/catalog/{package_id}`

Soft-delete the package. `204 No Content`.

The underlying object-storage `.scaipkg` files are kept for audit; only the `CatalogPackage` row is marked deleted.

Author / tenant admin / super admin.
