> ## Documentation Index
> Fetch the complete documentation index at: https://docs.craftkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish a catalog

> POST /v1/embed/catalogs — publish a named, versioned variable catalog for the embed picker.

<Info>**POST** `https://api.craftkit.dev/v1/embed/catalogs`</Info>

Publish a named variable catalog — the tree of namespaces, fields, and loops that powers the
embed's variable picker. Each call with the same `name` creates the **next version** and marks it
current; the previous version is archived, never deleted. Sessions reference a catalog by name via
[`catalogRef`](/api-reference/create-session).

## Authorization

<ParamField header="Authorization" type="string" required>
  `Bearer ck_live_…` — a project API key for a project with embed enabled.
</ParamField>

## Body

<ParamField body="name" type="string" required>
  Catalog name. Lowercase letters, numbers, and hyphens (`^[a-z0-9][a-z0-9-]*$`, ≤120 chars).
  Reusing an existing name publishes the next version.
</ParamField>

<ParamField body="catalog" type="object" required>
  The full catalog payload.

  <Expandable title="catalog">
    <ParamField body="allowCustom" type="boolean" default="false">
      When false, the picker hides the "+ Custom variable" affordance.
    </ParamField>

    <ParamField body="namespaces" type="CatalogNamespace[]" default="[]">
      Grouped scalar fields. Each: `key`, `label`, optional `icon`, and `fields[]` (≥1).
    </ParamField>

    <ParamField body="loops" type="CatalogLoop[]" default="[]">
      Repeating blocks. Each: `key`, `label`, `itemFields[]` (≥1), optional `previewData` (≤10 rows).
    </ParamField>
  </Expandable>
</ParamField>

A **CatalogField** (used in `namespaces[].fields` and `loops[].itemFields`) is:

<ParamField body="key" type="string" required>Dot-path key (`customer.name`). Pattern `^[a-zA-Z_][a-zA-Z0-9_.]*$`, ≤160 chars.</ParamField>
<ParamField body="label" type="string" required>Display name in the picker (1–160 chars).</ParamField>
<ParamField body="dataType" type="string" required>One of `text`, `longtext`, `number`, `currency`, `date`, `datetime`, `boolean`, `image`, `url`, `email`.</ParamField>
<ParamField body="required" type="boolean" default="false">Client-side hint; form-fill blocks submit if left blank. Server-side enforcement still uses the template's variable definition.</ParamField>
<ParamField body="format" type="string">Formatting hint (`currency:EUR`, `date:DD/MM/YYYY`), ≤60 chars.</ParamField>
<ParamField body="description" type="string">Helper text shown under the field, ≤280 chars.</ParamField>
<ParamField body="previewData" type="scalar">Dummy value for the live preview / input placeholder.</ParamField>

## Response

`201` when the version is published.

<ResponseField name="id" type="string">Catalog row UUID for the new version.</ResponseField>
<ResponseField name="name" type="string">The catalog name you passed.</ResponseField>
<ResponseField name="version" type="number">Monotonically increasing version number within this name.</ResponseField>

## Errors

| Status | code                    | Meaning                                                             |
| ------ | ----------------------- | ------------------------------------------------------------------- |
| 401    | `missing_authorization` | No `Authorization: Bearer` header.                                  |
| 401    | `invalid_credentials`   | Key not found, revoked, or embed not enabled.                       |
| 400    | `invalid_json`          | Body is not valid JSON.                                             |
| 422    | `invalid_request`       | `name`/envelope or `catalog` failed validation (`issues` included). |
| 500    | `internal_error`        | Auth check or catalog persistence threw — retry.                    |

```bash cURL theme={null}
curl -X POST https://api.craftkit.dev/v1/embed/catalogs \
  -H "Authorization: Bearer $CRAFTKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-catalog",
    "catalog": {
      "allowCustom": false,
      "namespaces": [
        {
          "key": "customer",
          "label": "Customer",
          "icon": "user",
          "fields": [
            { "key": "customer.name",  "label": "Customer name",  "dataType": "text",  "previewData": "Acme Corp" },
            { "key": "customer.email", "label": "Customer email", "dataType": "email", "previewData": "hello@acme.com" }
          ]
        }
      ],
      "loops": [
        {
          "key": "order.items",
          "label": "Order items",
          "itemFields": [
            { "key": "name",  "label": "Product",    "dataType": "text",     "previewData": "Widget A" },
            { "key": "qty",   "label": "Quantity",   "dataType": "number",   "previewData": 2 },
            { "key": "price", "label": "Unit price", "dataType": "currency", "previewData": 49.99 }
          ]
        }
      ]
    }
  }'
```

```json 201 theme={null}
{
  "id": "0193c2c3-1a2b-7c3d-8e4f-aabbccddeeff",
  "name": "my-catalog",
  "version": 1
}
```

<Tip>
  Reference the published catalog when minting a session:
  [`catalogRef: { name: "my-catalog" }`](/api-reference/create-session) resolves to the current
  version. Add `version` to pin an archived one.
</Tip>
