---
title: 'Tutorial: Publish to your tenant catalog'
path: tutorials/publish-to-tenant-catalog
status: published
---

# Tutorial: Publish to your tenant catalog

Package a flow as a `.scaipkg`, publish it to your tenant's private catalog, and install it on another flow's seat.

## What the catalog is

A per-tenant private library of flow packages. Each package contains the flow JSON, the compiled YAML manifest, package metadata, and an optional README. Other authors in your tenant can browse it from the canvas's Catalog modal and one-click install a copy with a fresh id.

It's intentionally separate from the public-marketplace concept (ScaiExchange, deferred to post-GA). Catalog is private, scoped to your tenant.

## Steps

### 1. Get the flow into shape

Open the flow in the canvas. Make sure it compiles (live preview pane shows no errors) and ideally test it locally.

### 2. Open Publish

Toolbar → **Catalog…** → **Publish current flow** tab.

The form fields are pre-filled from the flow's metadata:

- **Package id** — tenant-scoped slug (`customer-support-v2`). Auto-derived from the flow name; lowercase, dash-separated.
- **Name** — human-readable display name.
- **Version** — semver. Defaults to `0.1.0`. Republishing with the same version auto-bumps the patch number.
- **Description** — short blurb shown in the Browse tab.
- **README** — optional. Markdown; rendered on the package detail page.

### 3. Publish

Click **Publish**. ScaiFlow:

1. Compiles the flow to YAML (server-side — catches breakage before packaging).
2. Bundles `flow.json` + `manifest.yaml` + `package.json` + optional `README.md` into a deterministic `.scaipkg` zip.
3. Writes it to object storage at `catalog/{tenant_id}/{package_id}/v{version}.scaipkg`.
4. Upserts a `CatalogPackage` row indexing the latest version.

If compile fails (broken flow), publish refuses with the compile error inline — no half-published package.

### 4. Browse + install (as another author)

Anyone in your tenant with `scaicore:view` opens **Catalog…** → **Browse** tab. They see your package with name, slug, version, description.

Click **Install** → a fresh copy loads onto their canvas as an unsaved flow:

- Fresh `flow.id` (so saving creates a new flow, doesn't overwrite the original).
- Name: `Copy of {original name}`.
- All other content (nodes, edges, config) verbatim from the package.

They can then customize and save it as their own flow.

### 5. Bump versions

Republish with a higher version number:

```text
Version: 0.2.0  (was 0.1.0)
```

Or with the same version (`0.1.0`) — the patch number auto-bumps to `0.1.1`. Both create a new package version at `catalog/{tenant_id}/{package_id}/v{new_version}.scaipkg`. Old versions stay in object storage; the catalog index points at the newest.

## CLI alternative

The `scaiflow publish` CLI writes a `.scaipkg` to disk without uploading:

```bash
scaiflow publish my-flow.flow.json \
  --package-id customer-support \
  --name "Customer Support" \
  --version 0.2.0 \
  --description "Triage + KB + HITL review" \
  --readme README.md \
  --out customer-support-v0.2.0.scaipkg
```

Useful for distributing packages over channels other than the catalog (Slack, email, S3). The recipient can install by either:

- Manually `unzip`-ing and importing the `flow.json` via the canvas Import button.
- Future: a "Upload .scaipkg" path in the Catalog modal (planned).

## Visibility (groups)

By default, a published package is visible to **everyone in your tenant**. To restrict:

In the Publish form, set **Visibility** to `groups` and list one or more ScaiKey group IDs. Only members of any listed group will see the package in the Browse tab; non-members get a 404 on direct install attempts.

The package's author always sees their own packages regardless of visibility. Super admins see everything across all tenants.

## Remove a package

In the Browse tab, click the `×` next to a package you authored (or are admin on). Confirms with a prompt. Marks the catalog row deleted; the underlying object-storage `.scaipkg` files are kept for audit/undelete.

## What's in a `.scaipkg`

```text
my-package-v0.2.0.scaipkg
├── flow.json            # the canonical flow shape
├── manifest.yaml        # the compiled ScaiCore YAML
├── package.json         # name, version, description, scaiflow_version
└── README.md            # optional, your prose
```

Deterministic byte layout — sorted-key JSON, fixed line endings — so the same input always produces the same zip bytes. Hash-pinnable for cache invalidation or signature workflows.

## Next

- **[REST API: Catalog](../reference/rest-api/catalog)** — automate publishing from CI.
- **[CLI: publish](../reference/cli#publish)** — full flag set.
