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 / availabilityCore types
| Type | Purpose | Key rules |
|---|---|---|
LocalizedText | Any display string | Plain string (in defaults.language) or a map of BCP-47 tags to strings. |
Price | Money | Integer minor units: {"amount": 450, "currency": "USD"} is $4.50. currency falls back to defaults.currency. |
PriceDelta | Modifier adjustment | Same as Price but the amount may be negative (“no cheese −0.50”). |
Variant | Purchasable form | Mutually exclusive sizes/forms with full prices. When present, they replace the item’s base price. |
ModifierGroup | Option set | minSelections > 0 makes it required; maxSelections: 1 makes it single-choice; omitted maxSelections means unlimited. |
Availability | Schedule | Day-of-week windows in venue.timezone, plus optional startDate/endDate bounds. Omitted means “always”. |
allergens / dietaryLabels | Filterable facts | Closed enums (EU 14 + US FDA superset). Free-form labels go in tags instead. |
extensions | Vendor data | Map whose keys must start with x-. The only place arbitrary properties are allowed. |
2. Conformance: authoring (producers)
- MUSTEmit documents that validate against
menu.schema.json. The schema setsadditionalProperties: falseeverywhere, so misspelled or invented fields fail validation rather than being silently carried along. - MUSTExpress every price in integer minor units of its currency, using the ISO 4217 exponent —
450is USD 4.50,120is JPY 120. Never emit floating-point money. - MUSTEnsure every price resolves to a currency: either an inline
currencyor adefaults.currency. A document containing a price with neither is non-conformant even though it validates. - MUSTEnsure every
modifierGroupIdsentry references theidof a group in the top-levelmodifierGroupsarray. Dangling references are non-conformant. - MUSTKeep
idvalues unique within their collection and stable across exports, so consumers can diff successive documents.skuis the place for an external POS identifier;idbelongs to this document. - MUSTPut vendor-specific data only under
extensionswithx--prefixed keys. Extensions MUST NOT change the meaning of core fields — a document is still correct when all extensions are ignored. - SHOULDSet
defaults.languagewhenever plain (non-map) strings are used, andvenue.timezonewhenever any availability uses times of day. - SHOULDOmit the base
pricewhen an item hasvariants— the variants replace it, so a base price is dead data at best and contradictory at worst. - SHOULDOmit optional fields entirely rather than emitting empty arrays, empty objects, or empty strings. Absence means “not stated”, never “present but empty”.
- SHOULDUse
available: falsefor temporary unavailability (an 86’d dish) and actual removal for items no longer sold. List allergens the item contains or may contain; when in doubt, include the allergen.
3. Conformance: parsing (consumers)
- MUSTResolve currency as
price.currency→defaults.currency; and language as the requested tag →defaults.language→ any available translation. If a price resolves to no currency, treat that item as invalid rather than guessing. - MUSTTreat
variantsas replacing the basepricewhenever both are present, and treat absence of an allergen as no claim — never as a guarantee that the item is free of it.dietaryLabelsare positive claims by the producer. - MUSTInterpret schedules in
venue.timezone, withstartTimeinclusive andendTimeexclusive; anendTimeat or beforestartTimewraps past midnight. Omitted availability means always available; item-level availability further narrows its menu’s. - MUSTExclude
available: falseitems from ordering flows. They MAY still be displayed as sold out. - MUSTWhen re-emitting a document, preserve
extensionsand translations in languages the consumer does not itself handle. Round-tripping a document through a conforming system loses nothing. - SHOULDValidate incoming documents against the schema and reject those that fail, rather than repairing them silently. When resolving modifiers, apply shared groups (in
modifierGroupIdsorder) before inline groups; a dangling reference SHOULD be skipped with a warning, not treated as fatal. - SHOULDOn duplicate
ids within a collection, either reject the document or keep the first occurrence — consistently. Do not merge duplicates. - MAYIgnore fields irrelevant to the consumer’s purpose (a print renderer may skip
skuandnutrition), provided ignored data survives round-tripping per the rule above.
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).