Flights (flight_picker)
Let the person choose one flight itinerary. The page records the choice; it does not book or hold a seat.
When to use it: The person chooses one flight from search results. Each option shows airline, times, stops and price. The page only records the choice: book it yourself afterwards, and do not tell the person it is booked until you have.
When not to use it: Do not use it before you have real search results, or to show one flight that is already chosen: say that in chat.
Gather first:
- Search for flights first, so you offer real itineraries with real prices.
- The person's dates, origin and destination, and how many travellers the prices are for.
Writing good content:
- Times are ISO 8601 with the UTC offset of the airport where the event happens, and
timeZoneis that airport's IANA zone: depart"2026-11-03T08:15:00-08:00"with"America/Los_Angeles", arrive in Lisbon with"Europe/Lisbon". Never convert times to the person's zone; the page shows local airport time. airportis the 3-letter IATA code; addcityfor clarity.stops: []for nonstop. For connections, list each stop with its layover in minutes.priceis the total for the itinerary including taxes, with an ISO 4217 currency code.badgesfor at most two honest highlights ("Cheapest", "Fastest", "Nonstop").- Use the airline's or search result's own identifier as
idso you can act on exactly what was picked.
Fields
Every page also has version (always 1), title, and optionally description, branding, cover (a header photo) and expiresAt; see Page fields. These are the fields of page (the page type and its data):
| Field | Description |
|---|---|
options | object[], required. Flight options in display order (1-30). Limits: 1-30 items. |
options[].id | string, required. Stable identifier you choose. It is returned in the result, so keep it meaningful to you. Limits: matches ^[A-Za-z0-9_-]{1,64}$. |
options[].airline | string, required. Marketing airline name, e.g. "Skyward Air". Required. Limits: 1-60 characters. |
options[].flightNumbers | string[]. Optional flight number for each segment, in order, e.g. ["SA 123", "SA 456"]. Include when known. Limits: 1-6 items. |
options[].depart | object, required. Departure airport and time. airport and time are required. |
options[].depart.airport | string, required. IATA airport code, e.g. "SFO". Limits: matches ^[A-Z]{3}$. |
options[].depart.city | string. Optional city name shown next to the code. Include when known; omit otherwise. Limits: 1-60 characters. |
options[].depart.time | ISO 8601 timestamp, required. Scheduled time as ISO 8601 with UTC offset, e.g. "2026-11-03T08:15:00-08:00". |
options[].depart.timeZone | string. Optional IANA time zone of the airport, e.g. "America/Los_Angeles". Include when known; omit otherwise and the time is shown at the UTC offset written in time. |
options[].arrive | object, required. Final arrival airport and time. airport and time are required. |
options[].arrive.airport | string, required. IATA airport code, e.g. "SFO". Limits: matches ^[A-Z]{3}$. |
options[].arrive.city | string. Optional city name shown next to the code. Include when known; omit otherwise. Limits: 1-60 characters. |
options[].arrive.time | ISO 8601 timestamp, required. Scheduled time as ISO 8601 with UTC offset, e.g. "2026-11-03T08:15:00-08:00". |
options[].arrive.timeZone | string. Optional IANA time zone of the airport, e.g. "America/Los_Angeles". Include when known; omit otherwise and the time is shown at the UTC offset written in time. |
options[].stops | object[]. Optional connection airports in order. Use [] for a nonstop flight; omit when you do not know the stops. Limits: at most 4 items. |
options[].stops[].airport | string, required. IATA airport code, e.g. "SFO". Limits: matches ^[A-Z]{3}$. |
options[].stops[].city | string. Optional city name. Include when known; omit otherwise. Limits: 1-60 characters. |
options[].stops[].layoverMinutes | integer, required. Time on the ground at this stop, in minutes. Limits: 1-2880. |
options[].price | object, required. Total price for the itinerary, including taxes. Required. |
options[].price.amount | number, required. Amount in major units, e.g. 249.99 for $249.99. Limits: 0-10000000. |
options[].price.currency | string, required. ISO 4217 currency code, uppercase, e.g. "USD", "EUR". |
options[].cabin | "economy" | "premium_economy" | "business" | "first". Optional cabin class. Include when known; omit otherwise. |
options[].badges | string[]. Optional short highlights such as "Cheapest" or "Fastest" (up to 3). Limits: at most 3 items. |
options[].notes | string. Optional short note, e.g. "Bags not included". Limits: 1-200 characters. |
options[].recommended | boolean. Optional. Mark at most one option you recommend, when you have a reason: true shows a "Recommended" label on it. Omit on the others. |
options[].recommendedReason | string. Optional short reason shown with the label, e.g. "Nonstop, and only $30 more". Only with recommended: true. Limits: 1-80 characters. |
Why these are required: Who flies, from where to where, when, and for how much is the least a person needs to pick a flight.
When optional fields are left out: Times are shown at the UTC offset written in the timestamp; no stops line (unknown, so not "Nonstop"); no cabin, flight numbers, badges or note; no "Recommended" label. At most one option may set recommended: true, and recommendedReason is allowed only on that option.
Minimal example
Only the required fields:
{
"version": 1,
"title": "Pick a flight to Denver",
"page": {
"type": "flight_picker",
"options": [
{
"id": "skw204",
"airline": "Skyward Air",
"depart": {
"airport": "SEA",
"time": "2026-11-12T07:40:00-08:00"
},
"arrive": {
"airport": "DEN",
"time": "2026-11-12T11:05:00-07:00"
},
"price": {
"amount": 164,
"currency": "USD"
}
},
{
"id": "blu88",
"airline": "Bluefin Airlines",
"depart": {
"airport": "SEA",
"time": "2026-11-12T13:15:00-08:00"
},
"arrive": {
"airport": "DEN",
"time": "2026-11-12T16:50:00-07:00"
},
"price": {
"amount": 139,
"currency": "USD"
}
}
]
}
}Full example
{
"version": 1,
"title": "Choose your flight to New York",
"description": "Tuesday, November 3 · 1 adult · Prices include taxes.",
"branding": {
"name": "Juniper Travel",
"accentColor": "#0b7a5c"
},
"page": {
"type": "flight_picker",
"options": [
{
"id": "zq101",
"airline": "Skyward Air",
"flightNumbers": [
"ZQ 101"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T06:00:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-03T14:25:00-05:00"
},
"stops": [],
"price": {
"amount": 219,
"currency": "USD"
},
"cabin": "economy",
"badges": [
"Fastest"
],
"recommended": true,
"recommendedReason": "Nonstop, and only $30 more than the cheapest"
},
{
"id": "bf220",
"airline": "Bluefin Airlines",
"flightNumbers": [
"BF 220",
"BF 1408"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T09:10:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-03T19:55:00-05:00"
},
"stops": [
{
"airport": "DEN",
"city": "Denver",
"layoverMinutes": 65
}
],
"price": {
"amount": 189,
"currency": "USD"
},
"cabin": "economy",
"badges": [
"Cheapest"
],
"notes": "Carry-on only; checked bags extra."
},
{
"id": "ap9",
"airline": "Aurora Pacific",
"flightNumbers": [
"AP 9"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T22:30:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-04T07:05:00-05:00"
},
"stops": [],
"price": {
"amount": 412.5,
"currency": "USD"
},
"cabin": "premium_economy",
"notes": "Overnight flight with lie-flat seats."
}
]
}
}Result
When the person submits, the result has these fields:
| Field | Description |
|---|---|
selectedOptionId | string, required. id of the chosen flight option. Limits: matches ^[A-Za-z0-9_-]{1,64}$. |
For the full example above, a result could be:
{
"selectedOptionId": "zq101"
}Send it
eve, AI SDK and MCP
With @pagesystems/eve, @pagesystems/ai-sdk or the MCP server, the model calls get_page_type_schema with "flight_picker" (the same JSON as GET /v1/page-types/flight_picker), then create_page with this input (no version or branding; branding comes from your dashboard):
{
"title": "Choose your flight to New York",
"description": "Tuesday, November 3 · 1 adult · Prices include taxes.",
"page": {
"type": "flight_picker",
"options": [
{
"id": "zq101",
"airline": "Skyward Air",
"flightNumbers": [
"ZQ 101"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T06:00:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-03T14:25:00-05:00"
},
"stops": [],
"price": {
"amount": 219,
"currency": "USD"
},
"cabin": "economy",
"badges": [
"Fastest"
],
"recommended": true,
"recommendedReason": "Nonstop, and only $30 more than the cheapest"
},
{
"id": "bf220",
"airline": "Bluefin Airlines",
"flightNumbers": [
"BF 220",
"BF 1408"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T09:10:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-03T19:55:00-05:00"
},
"stops": [
{
"airport": "DEN",
"city": "Denver",
"layoverMinutes": 65
}
],
"price": {
"amount": 189,
"currency": "USD"
},
"cabin": "economy",
"badges": [
"Cheapest"
],
"notes": "Carry-on only; checked bags extra."
},
{
"id": "ap9",
"airline": "Aurora Pacific",
"flightNumbers": [
"AP 9"
],
"depart": {
"airport": "SFO",
"city": "San Francisco",
"timeZone": "America/Los_Angeles",
"time": "2026-11-03T22:30:00-08:00"
},
"arrive": {
"airport": "JFK",
"city": "New York",
"timeZone": "America/New_York",
"time": "2026-11-04T07:05:00-05:00"
},
"stops": [],
"price": {
"amount": 412.5,
"currency": "USD"
},
"cabin": "premium_economy",
"notes": "Overnight flight with lie-flat seats."
}
]
}
}REST
curl https://pagesystems.ai/v1/pages \
-H "Authorization: Bearer $PAGE_SYSTEMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"spec":{"version":1,"title":"Choose your flight to New York","description":"Tuesday, November 3 · 1 adult · Prices include taxes.","branding":{"name":"Juniper Travel","accentColor":"#0b7a5c"},"page":{"type":"flight_picker","options":[{"id":"zq101","airline":"Skyward Air","flightNumbers":["ZQ 101"],"depart":{"airport":"SFO","city":"San Francisco","timeZone":"America/Los_Angeles","time":"2026-11-03T06:00:00-08:00"},"arrive":{"airport":"JFK","city":"New York","timeZone":"America/New_York","time":"2026-11-03T14:25:00-05:00"},"stops":[],"price":{"amount":219,"currency":"USD"},"cabin":"economy","badges":["Fastest"],"recommended":true,"recommendedReason":"Nonstop, and only $30 more than the cheapest"},{"id":"bf220","airline":"Bluefin Airlines","flightNumbers":["BF 220","BF 1408"],"depart":{"airport":"SFO","city":"San Francisco","timeZone":"America/Los_Angeles","time":"2026-11-03T09:10:00-08:00"},"arrive":{"airport":"JFK","city":"New York","timeZone":"America/New_York","time":"2026-11-03T19:55:00-05:00"},"stops":[{"airport":"DEN","city":"Denver","layoverMinutes":65}],"price":{"amount":189,"currency":"USD"},"cabin":"economy","badges":["Cheapest"],"notes":"Carry-on only; checked bags extra."},{"id":"ap9","airline":"Aurora Pacific","flightNumbers":["AP 9"],"depart":{"airport":"SFO","city":"San Francisco","timeZone":"America/Los_Angeles","time":"2026-11-03T22:30:00-08:00"},"arrive":{"airport":"JFK","city":"New York","timeZone":"America/New_York","time":"2026-11-04T07:05:00-05:00"},"stops":[],"price":{"amount":412.5,"currency":"USD"},"cabin":"premium_economy","notes":"Overnight flight with lie-flat seats."}]}}}'Validation errors
A spec that does not validate is refused with 400 invalid_spec. The message names each problem and its path, so a model can fix the spec and try again. For example:
Leaving out the required field options:
✖ Invalid input: expected array, received undefined
→ at page.optionsMisspelling a field name (unknown keys are rejected):
✖ Unrecognized key: "colour"
→ at page