The specification
Questi contenuti non sono ancora disponibili nella tua lingua.
This page states the normative requirements for an ORDER 0.1 document — the rules the schema alone cannot express. The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.
The document envelope
Section titled “The document envelope”An ORDER document is a single JSON object with four required top-level properties: order_version, modules, generator, and restaurant. Everything else — rooms, tables, table_groups, service_periods, closures, menus, labels, reservations — is an optional array a producer includes only for the data it actually has.
Distribution formats
Section titled “Distribution formats”An ORDER export takes one of two forms.
An ORDER Document is the JSON object itself, typically a file named order.json. A media object’s url is an absolute URL, so the document is self-contained and needs no companion files to be read.
An ORDER Bundle is a .order.zip archive containing order.json plus a media/ directory holding the files those URLs point to. Inside a Bundle, a media object additionally carries path — a location relative to the archive root — alongside url. path is meaningful only inside a Bundle; a bare Document omits it.
The Bundle exists because a plain JSON export is dead image links the moment a restaurant leaves the exporting platform’s CDN — which is not a migration. A producer offering export SHOULD make the Bundle form available, not the Document alone.
Identity
Section titled “Identity”ref is document-scoped and unique across the entire document. Every entity that carries a ref — restaurant, room, table, table group, service period, turn, closure, menu, section, item, note, label, reservation — draws from one shared namespace: no two of them, of any kind, may carry the same ref. A duplicate makes the entity graph ambiguous — a pointer such as closure.table_refs could then resolve to more than one entity — so the reference validator rejects it as an error (duplicate_ref), not a warning.
Within that one document, ref identifies an entity and nothing more. Consumers MUST NOT treat a ref as globally meaningful — the same string in two different exports may refer to two different tables, and nothing makes ref unique across documents.
source_id, present on a few entities (tables, menus, menu items, reservations), is informational: a breadcrumb back to the producer’s own internal record, useful when a human is debugging an import. It MUST NOT be used for cross-document identity — treat it as an opaque string a reader is free to ignore.
Referential integrity
Section titled “Referential integrity”Pointer fields hold the ref of another entity in the same document: table.room_ref, table_group.table_refs, closure.table_refs, closure.turn_ref, pairing.item_ref, menu_item.label_refs, menu_note.anchor.ref, reservation.table_refs, and reservation.turn_ref.
A producer MUST NOT emit a dangling pointer — one whose ref names no entity present in the document. When the target goes away (a table is removed, say), the producer either drops the pointer — widening scope per that field’s rules — or omits the referring entity entirely; it never leaves the pointer hanging. A consumer that nonetheless encounters a dangling ref MUST treat it as absent and SHOULD surface a warning (the reference validator emits dangling_ref). This stays advisory rather than an error precisely because a producer MAY legitimately omit a whole collection — a reservations-only export carries no tables — so a pointer into an absent collection is intended, not corrupt.
For every *_refs array, absent and empty are equivalent: both mean “no entities referenced,” taking whatever widening the field defines (an empty or absent closure.table_refs closes the whole restaurant; an empty or absent reservation.table_refs is unassigned). A producer need not emit an empty array to mean what omitting the field already means.
Versioning
Section titled “Versioning”order_version is MAJOR.MINOR.
The 0.x line is pre-release and unstable. While the major version is 0, the format MAY change in breaking ways between minor versions — a 0.2 document is not guaranteed to validate under a 0.1 schema — and none of the compatibility guarantees below are in force yet. Treat 0.x as a draft to build and review against, not a frozen contract to ship long-lived data on.
From 1.0 onward, two guarantees take effect and hold for the life of the major version:
- A minor version MUST NOT loosen an existing constraint. It MAY add an optional property, or tighten (narrow an enum, add a validated format), but it MUST NOT widen or remove a constraint a prior minor enforced. This is what makes the number meaningful: a document valid under
1.0stays valid under1.0, and tighteningmoneyto two decimals in a1.1— which would break a valid1.0document — is forbidden. - A reader MUST accept any document of an equal-or-lower minor within the same major. A
1.1reader reads a1.0document; a1.0reader reads the1.0-shaped core of a1.1document (forward compatibility, below). Neither rejects the other over the version number alone.
Minor versions are therefore additive: a 1.1 document is a superset of what a 1.0 reader understands, never a breaking change to it. Adding an optional property is neither a loosening nor a tightening of any existing constraint — it is the additive middle the two rules leave room for.
A reader MUST ignore any property it does not recognise rather than failing to process the document because of it. The published schema is built to make this work on any validator, not only the reference one: it declares no additionalProperties: false, so a stock JSON Schema validator accepts unknown and future-minor properties, and a 1.1 document validates against the 1.0 schema. This is what lets a 1.0 reader consume the 1.0-shaped core of a document a newer producer stamped 1.1. The reference validator still reports each unrecognised property as an unknown_property warning — so a genuine typo surfaces without the document being rejected (see Conformance).
Extensions
Section titled “Extensions”Any object in the schema MAY carry properties named x_<vendor> (matching ^x_[a-z0-9_]+$) — for example x_eighty_six. These are vendor extensions: a place to carry proprietary data without forking the schema.
Validators MUST ignore x_ properties. They are the one kind of unrecognised property that produces no warning at all — every other unrecognised property produces an unknown_property warning (see Conformance). x_ properties are exempted entirely because they are declared as extension points, not merely tolerated as noise.
Geometry
Section titled “Geometry”Geometry is the part of the schema eighty-six’s own implementation had left implicit, so this section is more prescriptive than the rest.
- A table’s
geometry.positionis its centre, not a corner. geometry.rotation_degis degrees clockwise, in the range[0, 360).- A table’s
shape.points(whenshape.typeispolygon) are relative togeometry.position—(0, 0)is the table’s own centre. - A room’s
floor_plan.outlineandunusable_areaspoints are absolute coordinates in the room’scoordinate_system— the opposite convention from a table’s shape. A producer that reuses one code path for both will get this wrong, and there is no way to tell the two conventions apart from the point values alone — the distinction lives entirely in which field they appear under. - A
coordinate_systemwhoseunitsispixelsSHOULD carrypixels_per_meter. When it is absent, the plan is unscaled, and readers MUST NOT infer a real-world distance from raw pixel values. The reference validator flags this with afloor_plan_unscaledwarning rather than rejecting the document — an unscaled plan is still a valid plan, just one a reader cannot measure.
ORDER draws a hard line between a rule and an instance.
Everything that describes a recurring pattern — service_period, turn, restaurant.public_hours — is wall-clock: opens/closes are a plain HH:MM interpreted in restaurant.timezone. This is deliberate — a wall-clock rule does not drift across a DST transition. “Lunch opens at 12:00” means 12:00 local on every date the rule applies, summer or winter alike.
Everything that describes one specific occurrence — a reservation’s starts_at/ends_at — is absolute: RFC 3339 with a mandatory offset (2026-07-20T19:30:00+02:00), never a naive, offset-less timestamp.
Cross-midnight windows MUST be expressed with ends_next_day: true. Every wall-clock window that can span midnight carries the flag — service_period, turn, and each restaurant.public_hours entry — so a late seating that opens 23:30 and closes 01:00 is expressible, and a period that opens 23:00 and closes 03:00 is unambiguous.
A consumer reading a finished document MUST NOT infer ends_next_day from closes < opens: it cannot know the producer’s storage encoding, so it must not guess. On a purely wall-clock reading, opens: "23:00" with closes: "03:00" and no flag is a window that closes four hours before it opens — meaningless, not “assumed cross-midnight.” A producer translating its own source is the one party that can know: where end < start is a deliberate cross-midnight encoding in that source, setting ends_next_day from it is not guessing, and is correct.
restaurant.public_hours (display/marketing hours) and service_periods (what is actually bookable) are distinct fields and MAY differ — a restaurant can be open for walk-ins outside its bookable windows, or advertise hours wider than what the booking system currently accepts. A difference is not an error. The reference validator does warn (bookable_outside_public_hours) when a service period’s bookable window is not covered by any public-hours window on the same day, since in practice that combination is more often configuration drift than a deliberate choice.
Every amount (price.amount) is a decimal string, never a float — a price is not a value that can be allowed to drift by a cent through a serialization round-trip. The pattern is ^-?\d+(\.\d{1,4})?$: zero to four fraction digits, with an optional leading - for a credit or discount line. There is no per-price currency field; currency is document-wide, via restaurant.currency (an ISO 4217 code) — and it is restaurant.currency, not the pattern, that governs how many of those fraction digits actually apply: "1200" for JPY (no minor unit), "6.00" for EUR (two), "0.500" for KWD (three).
This is deliberately wider than any single currency needs. Versioning guarantees a 1.1 document validates under the 1.0 schema — that guarantee only holds in the tightening direction, so the widest correct pattern has to ship in 1.0, since a later minor version could never loosen it without breaking 1.0 readers.
Translations
Section titled “Translations”translations keys are BCP-47 language tags (^[a-z]{2}(-[A-Za-z0-9]{2,8})*$ — for example en, it, pt-BR).
The source language — whatever the base name/description fields are actually written in — lives in those base fields directly and MUST NOT also appear as a key in translations. A document with default_language: "it" and Italian text already in name does not repeat that text under translations.it; translations carries only the other languages a producer has translated into.
Because translations’ keys are constrained by JSON Schema’s propertyNames keyword rather than additionalProperties, a key that does not match the BCP-47 pattern — for example eighty-six’s own reserved __ai marker key — is a hard schema error, not an unknown_property warning. This is the one place in the schema where an “extra” property is rejected outright rather than tolerated: propertyNames and additionalProperties are different keywords, and the reference validator downgrades only the latter to a warning. See Conformance for the general rule this is the exception to.
Modules
Section titled “Modules”A document MUST declare, in modules, every module whose data it actually carries. Concretely: a document containing a non-empty reservations array MUST include "reservations" in modules. The reference validator rejects a document that carries reservations without declaring the module.
modules is deliberately an open list, not an enum of the two modules this version defines. A 1.0 reader MUST be able to consume the core half of a document that also declares a module it does not recognise — the validator warns (unknown_module) but does not reject.
Menus and allergens
Section titled “Menus and allergens”menu_item.allergens is drawn from a fixed enum: the fourteen substances in EU FIC Regulation 1169/2011, Annex II, listed in Annex order (gluten, crustaceans, eggs, fish, peanuts, soybeans, milk, nuts, celery, mustard, sesame, sulphites, lupin, molluscs). This is a separate field from label_refs — allergens are a closed, regulatory list, while labels (Spicy, Chef's Choice, or any restaurant-defined dietary tag) are an open, restaurant-defined vocabulary. A producer MUST NOT fold an allergen into a free-form label as a substitute for the allergens field.
Security
Section titled “Security”An exporter MUST NOT emit, anywhere in an ORDER document: credentials of any kind, cancellation or confirmation tokens (the links or codes a guest uses to manage their own booking), or vendor-internal identifiers that are meaningful only inside the producer’s own system (session ids, internal database primary keys presented as if they were a ref). source_id is the one sanctioned way to say “this came from record X in our system” — even then, it MUST NOT be a value that grants access to anything on its own.
