Publish menu
POST /v1/publishCreates a publication snapshot of the current menu and makes it live:
all products with menuVisible: true (and their categories, compositions
and modifiers) are frozen into a new publication that guests see on the
public menu.
No request body is required.
Sync vs publish#
POST /v1/sync writes to the draft menu — guests
don't see those changes yet. POST /v1/publish takes what the draft looks
like right now and snapshots it as the live menu. The typical flow:
- One or more
synccalls (and/orcleanup). - One
publishcall at the end.
Publish once per batch
Publishing after every small sync creates needless snapshots. Batch your changes, then publish once — it also keeps you well inside the rate limits.
Products with menuVisible: false (including those deactivated by
cleanup) are excluded from the snapshot.
Publish is immediate
The new snapshot goes live for guests the moment the call returns — there is no review step. Verify the draft first (e.g. GET /v1/menu, and dry-run your sync/cleanup calls). If a publish does go wrong, roll back below.
Publication history & rollback#
The last 5 publications are retained per venue as a rollback buffer:
GET /v1/publications
POST /v1/publish/rollbackGET /v1/publications lists them newest-first with a compact summary
(current: true marks the live one):
{
"items": [
{
"id": "cml9x2f4a0001abcd1234efgh",
"publishedAt": "2026-07-05T12:00:00.000Z",
"current": true,
"summary": { "format": "full", "categories": 8, "products": 64, "banners": 2 }
}
],
"total": 1, "page": 1, "limit": 5, "totalPages": 1
}POST /v1/publish/rollback re-publishes a retained snapshot verbatim
as the new live menu (an empty body targets the previous publication;
send { "publicationId": "..." } to pick one):
{
"success": true,
"publicationId": "cml9...new",
"restoredFrom": "cml9...old",
"restoredFromPublishedAt": "2026-07-04T18:20:00.000Z"
}Rollback restores what GUESTS saw — it does not change your draft menu;
the next regular publish publishes the draft again. Errors:
404 PUBLICATION_NOT_FOUND (not in the retained 5),
400 NO_PREVIOUS_PUBLICATION, 400 PUBLICATION_ALREADY_CURRENT,
400 PUBLICATION_INCOMPATIBLE (a legacy-format snapshot that predates
this feature — publish fresh instead).
Example#
curl -X POST https://api.duck-hub.com/v1/publish \
-H "Authorization: Bearer dk_live_your_api_key"const response = await fetch('https://api.duck-hub.com/v1/publish', {
method: 'POST',
headers: { Authorization: 'Bearer dk_live_your_api_key' },
})
const result = await response.json()
console.log('Published:', result.publicationId)Response#
201 Created:
{
"success": true,
"publicationId": "cml9x2f4a0001abcd1234efgh"
}| Field | Meaning |
|---|---|
success | Always true on success |
publicationId | Id of the created publication snapshot |
Errors#
401— see Authentication429— see Rate limits