Specification · v1.2
Format specification
Roadbook is an open JSON interchange format for complete travel itineraries. This page is the complete entity-by-entity reference. Fields marked * are required. Any field not documented here MUST be ignored, never rejected by a player — this rule is what makes minor versions backward compatible.
Current stable specification:
Roadbook 1.2 — released 2026-08-08, additive amendments through 2026-08-22 · status
stable · new files declare
"formatVersion": "1.2" · 1.0 and 1.1 remain valid subsets (
changelog,
compatibility policy). Any application may implement this specification; no registration, API key or permission is required.
Conventions#
| Concept | Rule |
id | Free-form non-empty string, unique within its collection. Prefer short and stable ("d1", "b-x7k2"). |
| dates | ISO format YYYY-MM-DD (e.g. "2026-08-04"). |
| times | 24-hour "HH:MM" (ISO 8601 style, zero-padded: "09:00", "14:30"). The file always stores this canonical form; players display times localized to the reader's locale (2:30 PM for en-US) and edit them with native time components. |
| timezones | All times are local wall-clock times at the place where the item happens. Optional timezone fields (IANA, e.g. "Europe/Paris") at trip, day, item and transport-point level are metadata enabling cross-zone computations (flights). An item ending the next day uses endDate. Since 1.2. |
| images | Fields image, cover, voucher, icon: an absolute URL, a path relative to the file (e.g. medias/001.jpg inside a .roadbook.zip), or an opaque media reference of the player that wrote it (e.g. rbm://…, resolvable only by that player). Data-URLs are tolerated but discouraged: keep the document light, ship binaries next to it. |
| file | Extension .roadbook.json, UTF-8 encoded. Media type: application/json — no vendor-specific media type (such as application/vnd.roadbook+json) is registered; do not rely on one. A reader recognizes a Roadbook by the .roadbook.json extension and the root fields (formatVersion, days, members). |
| version | Root field formatVersion ("1.2"). Absent means "1.0". See versioning and the changelog. |
| prices | price fields are total amounts (all travelers); currency is ISO 4217 and defaults to the trip's currency (itself defaulting to "EUR"). Since 1.1. |
| enum values | Enumerated values are English tokens ("meal", "confirmed"…) and are part of the wire format — used verbatim in the file, translated by the player at display time. |
Root object — the trip#
| Field | Type | Description |
formatVersion | string | Format version ("1.0"). Absent = 1.0. |
currency | string | Default currency for all prices (ISO 4217). Absent = "EUR". Since 1.1. |
timezone | string | Default IANA timezone of the trip. Since 1.2. |
id * | string | Unique trip identifier. |
name * | string | Trip name. |
subtitle | string | Tagline. |
start * | date | First day. |
end * | date | Last day (≥ start). |
cover | image | Cover image. |
members * | Member[] | The travelers. |
days * | Day[] | The days, sorted by ascending date. |
bookings | Booking[] | Reservations (stays, activities…). |
checklists | Checklist[] | Check lists (luggage, documents…). |
contacts | Contact[] | Contacts and emergency numbers. |
logistics | LogisticsNote[] | Free-form logistics notes. |
apps | AppLink[] | Useful apps for the trip. |
people | Person[] | On-site people to meet: guides, drivers, hosts. Since 1.2. |
vehicles | Vehicle[] | Vehicles used during the trip. Since 1.2. |
brand | Brand | Organisation distributing this roadbook (tour operator, agency, company). Since 1.2 (added 2026-08). |
Brand — distributor branding#
Lets a tour operator, agency or company sign the roadbooks it hands to travelers, so that a licensed player shows its branding. Players without an active branding licence for the brand must ignore logo and colors; they may only display "by <name>". An absent or unverified signature is never an error: the roadbook simply renders unbranded.
| Field | Type | Description |
id * | string | Brand identifier registered with the player operator (lowercase kebab-case, e.g. "clubmed"). |
name * | string | Display name. |
logo | image | Logo, transparent background recommended. |
colors | object | { primary?, accent? }, hex colors; theme hints applied only under an active licence. |
website | string | Brand website (absolute URL). |
keyId | string | Identifier of the signing key (for rotation). |
signature | string | base64url Ed25519 signature of the UTF-8 string "<trip.id>|<brand.id>", made with the brand's private key. A player fetches the brand's public key from the operator and verifies it before theming; it binds this exact roadbook (id) to the brand, so copying the block into another file does not transfer the branding. |
Member — a traveler#
| Field | Type | Description |
id * | string | Member identifier. |
name * | string | Display name. |
role * | enum | owner (administers), editor (can modify), viewer (read-only). At least one owner is recommended. |
kind * | enum | adult, teen, child. |
age | number | Age (pricing, tickets). |
surpriseMode | enum | Spoiler protection: day = future days hidden for this member; activity = only the next item of the day is revealed. |
surprise | boolean | Deprecated — boolean ancestor of surpriseMode, kept for compatibility. |
phone / email | string | Contact details. Since 1.2. |
loyalty | array | Loyalty programs: { program, number }[] (frequent flyer, hotel status…). Since 1.2. |
dietary | string[] | Dietary restrictions (free tokens: vegetarian, halal, gluten_free…). Since 1.2. |
accessibility | string[] | Accessibility needs (free tokens: wheelchair…). Since 1.2. |
Person — someone to meet on-site#
Guides, drivers, hosts — referenced by personIds on items and bookings. Since 1.2.
| Field | Type | Description |
id * · name * | string | Identifier and display name. |
role | enum | guide, driver, host, contact, other. |
photo | image | Photo — to recognize the person at the meeting point. |
phone / email | string | Direct contact. |
messaging | array | { app, handle }[] — app is a free token (whatsapp, wechat, telegram…). |
languages | string[] | Spoken languages (ISO 639-1). |
org | string | Organization (tour operator, agency…). |
sign | string | Recognition sign ("name board GODON", "red umbrella"). |
note | string | Free note. |
Vehicle#
Referenced by route.vehicleId (day) and transport.vehicleId. Since 1.2.
| Field | Type | Description |
id * · name * | string | Identifier and display name ("Our Tesla", "MacBike rentals"). |
kind | enum | car, van, motorcycle, bike, camper, other. |
energy | enum | electric, gasoline, diesel, hybrid, plugin_hybrid, other. |
plate | string | License plate (often required by parking bookings). |
rental | object | { company?, ref?, pickup?, dropoff? }. |
TransportDetails — structured journey#
Attached to items (flight, train, drive, transfer…) and to transport bookings. Since 1.2.
| Field | Type | Description |
carrier / number | string | Airline / operator, and flight or train number ("Air France", "AF1234"). |
ref | string | PNR / booking reference. |
from / to | TransportPoint | { name?, address?, coords?, terminal?, gate?, platform?, timezone? } — the two ends of the journey (a transfer has a pickup and a destination). |
vehicleId | string | Vehicle used (references Vehicle.id). |
Day#
| Field | Type | Description |
id * | string | Day identifier. |
index * | integer | Day number (1 = first day), consistent with date order. |
date * | date | Date, within the trip period. |
title * | string | Label (e.g. "Bruges → Brussels"). |
intro | string | Narrative introduction for the day. |
timezone | string | Dominant IANA timezone of the day. Since 1.2. |
stayBookingId | string | Overnight accommodation (references a Booking.id). |
route | object | { km?, drive?, charge?, vehicleId? } — distance, human-readable driving time, EV charging plan, day's vehicle (vehicleId since 1.2). |
coords | object | { lat, lon, label } — the day's position (weather, map). |
items * | DayItem[] | The day's items, sorted by time. |
tip | string | Tip of the day. |
planB | string | Day-level fallback plan (free text). |
DayItem — an itinerary item#
| Field | Type | Description |
id * | string | Item identifier. |
time | time | Start time ("09:00", "14:30"). |
endTime | time | End time. |
endDate | date | End date when different from the day — overnight flight arriving D+1. Since 1.2. |
timezone / endTimezone | string | IANA timezones of start/end (times stay local wall-clock). Since 1.2. |
title * | string | Item title. |
desc | string | Free description. |
type * | enum | Activities & places: activity, nature, heritage, hike, sport, shopping, event, nightlife, wellness, viewpoint, tasting · Journeys: drive, flight, train, boat, ferry, transit, transfer, bike, walk · Food & energy: meal, charging, fuel · Daily life & business: break, laundry, admin, medical, work, meeting, conference · Lodging & free: checkin, checkout, free, night. Extended in 1.2. |
stars | 0–3 | Must-see level. |
place | string | Human-readable address or place. |
mapsQuery | string | Query for a mapping service (when place is not enough). |
coords | object | { lat, lon } — item position (offline maps, proximity). Since 1.2. |
memberIds | string[] | Travelers concerned (references Member.id); absent = everyone. Since 1.2. |
personIds | string[] | On-site people to meet (references Person.id). Since 1.2. |
transport | TransportDetails | Structured journey (see TransportDetails). Since 1.2. |
attachments | Attachment[] | Item-level documents { id, label, url }. Since 1.2. |
price / currency | number / string | Total price of the item (all travelers) and its ISO 4217 currency (absent = trip currency). Players may aggregate a trip budget. Since 1.1. |
priceEstimated | boolean | true = forecast price; absent/false = actual amount. Since 1.2. |
menuUrl | string | Link to the menu (restaurants). Since 1.1. |
outfit | string[] | Recommended outfit / gear. Known tokens: casual, sport, dressy, warm, windproof, waterproof, beach, swimsuit — other strings tolerated. Players may aggregate a day's values into a "what to wear today" hint. Since 1.1. |
outfitNote | string | Free-text outfit / gear details for this item ("walking shoes, rain jacket for the kids"). Players show known outfit tokens as chips and this note behind a details toggle. Since 1.2. |
bookingId | string | Linked reservation (references a Booking.id). |
critical | string | Critical warning text, shown as an alert (strict time slot, last entry, no refund…). Not a flag: omit it when nothing is critical. |
status * | enum | planned, done, skipped, replaced — the lived state, updated during the trip. |
alternatives | Alternative[] | Conditional fallbacks for this item. |
options | ItemOption[] | Interchangeable choices — see the distinction below. |
chosenOptionId | string | Selected option (references an entry of options). Absent until the choice is made. |
image | image | Header image. |
Options ≠ alternatives. options are interchangeable possibilities for the same slot (three candidate restaurants — decided on-site; the chosen option's image replaces the item's). alternatives are fallback plans triggered by an event: trigger ∈ rain, wind (with windThreshold km/h), cancellation, closure, fatigue, mood. A player may surface the alternative when the condition occurs.
Alternative — conditional fallback#
| Field | Type | Description |
id * | string | Identifier. |
trigger * | string | Trigger — known values: rain, wind, cancellation, closure, fatigue, mood. |
title * | string | The replacement activity. |
desc | string | Details. |
windThreshold | number | Wind threshold (km/h) for the wind trigger. |
ItemOption — on-site choice#
| Field | Type | Description |
id * | string | Identifier. |
title * | string | Option name. |
desc | string | Details (specialty, price…). |
place | string | Address. |
mapsQuery | string | Mapping query. |
coords | object | { lat, lon }. Since 1.2. |
menuUrl | string | Link to the menu (restaurants). Since 1.1. |
image | image | Replaces the item's image when this option is chosen. |
Booking — a reservation#
| Field | Type | Description |
id * | string | Identifier. |
type * | enum | stay, activity, parking, transport, restaurant, other. |
status * | enum | confirmed, to_book, optional, cancelled. |
title * | string | Venue / service name. |
hotelStars | number | Hotel star rating. |
dates | string | Human-readable dates ("nights of Aug 2–3"). |
checkin / checkout | time | Structured check-in / check-out times ("15h"). Since 1.2. |
breakfastIncluded | boolean | Breakfast included (stays). Since 1.2. |
rooms | Room[] | { label, memberIds? }[] — rooms and their occupants. Since 1.2. |
memberIds / personIds | string[] | Travelers concerned (absent = everyone) and on-site people (host, driver…). Since 1.2. |
transport | TransportDetails | Structured transfer (pickup → destination) — see TransportDetails. Since 1.2. |
payment | object | { status? (prepaid|deposit|onsite|invoice), deposit?, payer? (personal|company), note? }. Since 1.2. |
priceEstimated | boolean | true = forecast price. Since 1.2. |
address | string | Postal address. |
gpsAddress | string | Address to feed the GPS when different (parking entrance…). |
phone / email / website | string | Contact details. |
cancellation | string | Cancellation policy. |
instructions | string | Arrival instructions, access codes… |
price / currency | number / string | Total price of the booking and its ISO 4217 currency (absent = trip currency). Since 1.1. |
menuUrl | string | Link to the menu (restaurants). Since 1.1. |
refs | BookingRef[] | References: { label, value, sensitive? }. sensitive: true = masked by default. |
tickets | Ticket[] | Individual tickets. |
attachments | Attachment[] | Attached documents: { id, label, url }. |
reminder | object | { date, action, done? } — dated reminder ("book before…"). |
Ticket#
| Field | Type | Description |
id * | string | Identifier. |
label * | string | Label ("Adult", "Flight 2"). |
code * | string | Human-readable ticket number. |
holder | string | Displayed holder name (free string — prefer memberId for a strong link). |
memberId | string | Holder as a Member.id reference — survives renames. Since 1.2. |
date / time | date / time | Ticket-specific slot (individual entry time). Since 1.2. |
seat | string | Assigned seat. Since 1.2. |
validUntil | date | Use-by date (open vouchers). Since 1.2. |
payload | string | The content actually encoded in the QR / barcode, which may differ from the ticket number. This is what a player renders scannable — always store the real payload. |
format | enum | qr, code128, pdf417 (IATA boarding passes), aztec (European trains), ean13, datamatrix. Extended in 1.2. |
voucher | image | Original voucher image (available offline in a good player). |
Checklist#
| Field | Type | Description |
id * · title * · emoji | string | Identifier, title, decorative emoji. |
items * | array | Entries { id, label, done }. |
| Field | Type | Description |
id * · name * | string | Identifier and name. |
phone | string | Number, callable from a player. |
note | string | Context ("valid across the EU"). |
LogisticsNote#
| Field | Type | Description |
id * · title * · content * | string | Identifier, title, content (multiline). |
emoji | string | Decorative emoji. |
AppLink — a useful app#
| Field | Type | Description |
id * · name * | string | Identifier and name. |
desc | string | What it is useful for on this trip. |
url | string | Website. |
ios / android | string | App Store / Google Play links — a player shows the current platform's. |
icon / emoji | image / string | Icon (emoji as fallback). |
Integrity rules#
Beyond structure, the official validator checks:
id uniqueness within each collection · start ≤ end and day dates within the trip period · days sorted by date with consistent index values · valid references (bookingId, stayBookingId, chosenOptionId, memberIds, personIds, vehicleId, Ticket.memberId) · date and time formats.
Structural defects are errors (invalid file); reference or ordering inconsistencies are warnings (the file remains playable).
Implementations: roadbook-validate.mjs (ESM, zero dependencies), cli.mjs, web validator. Compatibility requirements for readers and writers: versioning. Worked examples of every entity: examples.