POST /v1/embed/catalogs
Publish a named variable catalog version via API.
Publish a new version of a named variable catalog. Each call creates the next version number and marks it as current — the previous version is archived but never deleted.
POST /v1/embed/catalogsQuick Start
curl
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",
"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": []
}
}'Node.js
const res = await fetch('https://api.craftkit.dev/v1/embed/catalogs', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRAFTKIT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'my-catalog',
catalog: {
allowCustom: false,
namespaces: [
{
key: 'customer',
label: 'Customer',
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: [],
},
}),
});
const { id, name, version } = await res.json();
// { id: "cat_01jt...", name: "my-catalog", version: 1 }Python
import os, requests
res = requests.post(
"https://api.craftkit.dev/v1/embed/catalogs",
headers={"Authorization": f"Bearer {os.environ['CRAFTKIT_API_KEY']}"},
json={
"name": "my-catalog",
"catalog": {
"allowCustom": False,
"namespaces": [
{
"key": "customer",
"label": "Customer",
"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": [],
},
},
)
result = res.json()
# {"id": "cat_01jt...", "name": "my-catalog", "version": 1}Request body
{
"name": "my-catalog",
"catalog": {
"allowCustom": false,
"namespaces": [ ... ],
"loops": [ ... ]
}
}| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Catalog name. Lowercase letters, numbers, and hyphens. Reusing an existing name creates the next version. |
catalog |
object | Yes | The full catalog payload — see schema below. |
Catalog schema
{
"allowCustom": false,
"namespaces": [
{
"key": "customer",
"label": "Customer",
"icon": "user",
"fields": [
{
"key": "customer.name",
"label": "Customer name",
"dataType": "text",
"previewData": "Acme Corp",
"description": "The company or individual name",
"format": "optional format string"
}
]
}
],
"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 }
],
"previewData": [
{ "name": "Widget A", "qty": 2, "price": 49.99 },
{ "name": "Widget B", "qty": 1, "price": 99.00 }
]
}
]
} namespaces[].fields object
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Dot-path key used in templates: customer.name. Pattern: ^[a-zA-Z_][a-zA-Z0-9_.]*$. |
label |
string | Yes | Display name in the variable picker. |
dataType |
string | Yes | One of text, longtext, number, currency, date, boolean, image, url, email. |
previewData |
scalar | — | Dummy value shown in the live preview. Doubles as input placeholder in form-fill embeds. |
format |
string | — | Formatting hint: currency:EUR, date:DD/MM/YYYY. |
description |
string | — | Helper text shown beneath the field in the variable picker. |
loops object
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Loop key: order.items. Compiles to {{#each order.items}}...{{/each}}. |
label |
string | Yes | Display name in the picker. |
itemFields |
array | Yes | Fields available inside the loop body. Same shape as namespace fields. |
previewData |
array | — | Dummy rows for the live preview (max 10). |
Response
201 Created
{
"id": "cat_01jt9wq...",
"name": "my-catalog",
"version": 1
}| Field | Type | Description |
|---|---|---|
id |
string | Catalog row UUID. Pass this as catalog_ref.id in a session if you want to pin a specific version. |
name |
string | The catalog name you passed. |
version |
number | Monotonically increasing version number within this name. |
Versioning
Calling POST /v1/embed/catalogs with the same name creates the next version:
v1 → archived
v2 → archived
v3 → current ← sessions referencing this name get v3Sessions minted with catalog_ref: { name: "my-catalog" } always resolve to the current version. To pin a session to a specific version, pass catalog_ref: { name: "my-catalog", version: 2 }.
Referencing the catalog in a session
After publishing, reference the catalog by name when minting a session:
{
"tenant": { "externalId": "org_123", "displayName": "Acme Corp" },
"actor": { "externalId": "usr_456", "displayName": "Jane Smith" },
"catalog_ref": { "name": "my-catalog" }
}Or inline a one-off catalog without publishing it first:
{
"tenant": { ... },
"actor": { ... },
"variable_catalog": { "allowCustom": false, "namespaces": [...], "loops": [] }
}See Embed sessions for the full session mint reference.
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_authorization |
No Authorization header. |
| 401 | invalid_credentials |
API key not found, revoked, or embed not enabled. |
| 400 | invalid_json |
Request body is not valid JSON. |
| 422 | invalid_request |
Body failed validation — check issues array for field-level details. |
Last revised: 2026-05-12