Product Registration
Before documentation can be deployed via the Upload API, a product must be registered. Products can be managed through the Admin UI, the API, or by placing JSON config files in the config directory.
Admin UI (Recommended)
The easiest way to register products is through the Admin UI at /admin/:
- Sign in with your email (one-time code)
- Navigate to the product management section
- Click "Create Product"
- Fill in the product details (name, display name, description, visibility)
- Save
See Admin UI for the full workflow.
API
Products can be created, updated, and deleted via the API. All mutating endpoints require authentication.
Create a Product
curl -X POST \
-H "Authorization: Bearer $SHELF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "configuration",
"displayName": "Cocoar.Configuration",
"description": "Reactive configuration for .NET",
"source": "upload",
"visibility": "public",
"tags": ["C#", ".NET"],
"showWhenEmpty": false
}' \
https://docs.cocoar.dev/_api/productsUpdate a Product
curl -X PUT \
-H "Authorization: Bearer $SHELF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"displayName": "Cocoar.Configuration",
"description": "Updated description",
"visibility": "preview",
"tags": ["C#", ".NET", "Configuration"],
"showWhenEmpty": true
}' \
https://docs.cocoar.dev/_api/products/configurationDelete a Product
# Remove registration only (docs files kept on disk)
curl -X DELETE \
-H "Authorization: Bearer $SHELF_API_KEY" \
https://docs.cocoar.dev/_api/products/configuration
# Remove registration AND all documentation files
curl -X DELETE \
-H "Authorization: Bearer $SHELF_API_KEY" \
"https://docs.cocoar.dev/_api/products/configuration?deleteData=true"JSON Config Files
Products can also be registered by placing JSON config files in {ConfigRoot}/products/, one file per product:
/data/config/
└── products/
├── configuration.json
├── capabilities.json
└── filesystem.jsonConfig File Format
Each JSON file describes one product:
{
"name": "configuration",
"displayName": "Cocoar.Configuration",
"description": "Reactive configuration for .NET",
"source": "upload",
"visibility": "public",
"tags": ["C#", ".NET"],
"showWhenEmpty": false
}| Field | Required | Description |
|---|---|---|
name | Yes | Product identifier, used in URLs and API calls |
displayName | No | Human-readable name for API responses and landing page |
description | No | Short description of the product |
source | No | Deployment source type. Default: "upload" |
visibility | No | "public" or "preview". Default: "public" |
openness | No | "OpenSource", "Proprietary" or "Unspecified". Drives a badge + filter on the landing page (display-only). Default: "Unspecified" |
repositoryUrl | No | Optional source-repository link; when set, the landing card shows a "Source ↗" link |
tags | No | List of free-form labels (e.g. ["C#", "UI"]). Used for filtering on the landing page |
showWhenEmpty | No | Show on landing page even without any deployed versions. Default: false |
TIP
The name field determines the URL path, not the filename. By convention, keep both in sync (e.g., configuration.json with "name": "configuration").
Visibility
The visibility field controls how a product appears on the landing page:
| Value | Behavior |
|---|---|
"public" | Shown on the landing page by default (if it has stable versions, or showWhenEmpty is set) |
"preview" | Hidden by default, shown when "Show preview" is toggled on |
Preview visibility is useful for products that are in development or not yet ready for general use. The documentation is still accessible via direct URL regardless of visibility.
Tags
Tags are free-form labels you can attach to a product (e.g. "C#", "UI", ".NET", "CLI"). They appear as chips on product cards, and the landing page provides a tag filter bar so visitors can narrow down the list to products that interest them.
Tags are stored as an array of strings. Shelf normalises them automatically: leading/trailing whitespace is trimmed, duplicates and empty values are removed, and the list is sorted alphabetically.
{
"tags": ["C#", ".NET", "Configuration"]
}Openness & Repository Link
The openness field marks whether a product's source is public — "OpenSource", "Proprietary", or "Unspecified" (the default). Marked products get a colored badge on the landing page and can be filtered by it, so open-source and proprietary documentation can be hosted side by side and told apart at a glance. It is a display marker only — it does not restrict access. To actually hide a product, mark it restricted instead.
Set repositoryUrl to show a discreet "Source ↗" link on the product card — typically for open-source products. Leave it empty and the card carries no repository link, which is the usual choice for proprietary products.
{
"openness": "OpenSource",
"repositoryUrl": "https://github.com/cocoar-dev/configuration"
}Show When Empty
By default, a product only appears on the landing page once it has at least one deployed version. Set showWhenEmpty: true to display the product card immediately — even before any documentation is uploaded. The card is rendered in a distinct teaser style with a "Coming soon" indicator.
This is useful for:
- Testing — verify the product is registered and visible before pushing the first version
- Teasers — announce upcoming documentation while it is still being written
{
"showWhenEmpty": true
}Live Reload
Config files are loaded at startup and monitored for changes. When you add, modify, or remove a config file, Shelf picks up the change automatically -- no restart needed.
Why Registration?
Product registration is required for the Upload API to prevent accidental creation of arbitrary products. Without registration, a typo in a CI pipeline (configration instead of configuration) would silently create a new product.
Manual deployment (copying files directly into the docs volume) does not require registration -- Shelf serves any product directory it finds, regardless of whether a config file exists.
API Integration
Registered products are visible via the API:
# List all registered products with their versions
curl https://docs.cocoar.dev/_api/products[
{
"name": "configuration",
"displayName": "Cocoar.Configuration",
"description": "Reactive configuration for .NET",
"source": "upload",
"visibility": "public",
"tags": ["C#", ".NET"],
"showWhenEmpty": false,
"latest": "v5",
"versions": ["v5", "v4"]
}
]