← Menu StudioSpecification

Menu Interchange Format 1.0

A vendor-neutral JSON format for exchanging restaurant and cafe menus between systems — points of sale, ordering apps, delivery platforms, and websites. The normative definition is menu.schema.json (JSON Schema draft 2020-12); this page explains the structure and states the conformance rules that JSON Schema alone cannot express.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119. A producer is any system that authors or emits documents; a consumer is any system that parses them.

Machine-readable & LLM-friendly

Point a tool or an LLM straight at the raw files — the schema and example are served live from the source of truth, so they never drift.

1. Document structure

One document describes one venue and any number of menus (Breakfast, Lunch, Drinks…). Menus contain sections, which hold items and may nest sub-sections to any depth.

document
├── schemaVersion         "1.0.0"
├── venue                 name, logo, timezone, contact info
├── defaults              currency + language inherited by everything below
├── modifierGroups[]      reusable option sets, referenced by id
└── menus[]
    ├── availability      day/time schedule, optional date range
    └── sections[]        nestable
        └── items[]
            ├── price | variants[]     one base price, or sizes/forms
            ├── modifierGroupIds[]     references to shared groups
            ├── modifierGroups[]       or inline, item-specific groups
            ├── dietaryLabels[] / allergens[] / spiceLevel
            ├── nutrition, media, tags, sku
            └── available / availability

Core types

TypePurposeKey rules
LocalizedTextAny display stringPlain string (in defaults.language) or a map of BCP-47 tags to strings.
PriceMoneyInteger minor units: {"amount": 450, "currency": "USD"} is $4.50. currency falls back to defaults.currency.
PriceDeltaModifier adjustmentSame as Price but the amount may be negative (“no cheese −0.50”).
VariantPurchasable formMutually exclusive sizes/forms with full prices. When present, they replace the item’s base price.
ModifierGroupOption setminSelections > 0 makes it required; maxSelections: 1 makes it single-choice; omitted maxSelections means unlimited.
AvailabilityScheduleDay-of-week windows in venue.timezone, plus optional startDate/endDate bounds. Omitted means “always”.
allergens / dietaryLabelsFilterable factsClosed enums (EU 14 + US FDA superset). Free-form labels go in tags instead.
extensionsVendor dataMap whose keys must start with x-. The only place arbitrary properties are allowed.

2. Conformance: authoring (producers)

3. Conformance: parsing (consumers)

4. Versioning

schemaVersion is semver. Within a major version, new releases only add optional fields or enum values — a valid 1.0 document is a valid 1.x document. Consumers MUST reject documents whose major version they do not support, and SHOULD validate against the newest 1.x schema available to them, since a validator built from an older minor may reject fields added in a newer one. Producers MUST declare the version whose features they actually use.

Unknown values of open-ended strings (tags, cuisine) carry no interoperability burden. New enum values (allergens, dietary labels) only appear with a minor version bump, so a consumer validating against the matching schema never sees a value it cannot list.

5. Validating

npm install          # ajv + ajv-formats
node validate.mjs    # checks examples/cafe-menu.json against menu.schema.json

Note that ajv’s optional strictRequiredlint flag false-positives on the schema’s “at least one of items / sections” idiom; leave it at its default (off).