{
  "openapi": "3.1.0",
  "info": {
    "title": "Feminiq Rides booking API",
    "version": "2.0.0",
    "description": "Two endpoints: a side-effect-free quote and the booking request. Every booking (airport transfer, custom ride or tour) is a request: it is stored and emailed to the driver and the customer, and no payment is taken. Feminiq Rides then confirms availability and the price personally and sends a payment request (SumUp) for 25% of the ride price (payment=deposit, the default) or the full price (payment=full); the booking is final once that payment is in, and any remainder is paid on the day of the ride. An API response never guarantees availability. Do not call POST /api/book without the customer's explicit approval of the shown quote and acceptance of the terms (https://www.feminiqrides.nl/terms.html). Rate limits: 5 booking requests and 30 quotes per client address per 15 minutes; HTTP 429 with Retry-After when exceeded. Every booking requires the signed quoteToken from /api/quote. Quotes expire after 15 minutes; expiry, changed rules or changed journeys (including the payment choice and the return date, time and flight) return HTTP 409 reviewRequired=true, requiring a fresh quote and renewed customer approval. Custom fares are honored within the validity window, never silently repriced at booking. Airport transfers and custom rides can include a return trip: returnTrip {date, time, flight?}. It is stored as a second booking (<bookingId>-ret) that is confirmed and paid together with the first ride (one payment request); tours cannot have one. Tour pickup is required and at most five tours may be selected. AI agents can also use the remote MCP server at https://www.feminiqrides.nl/mcp (tools list_services, get_quote, request_booking, get_booking_status, change_booking, cancel_booking), which runs these same two endpoints. Requests sent without the website (no Origin header) are confirmed by the customer by email before Feminiq Rides sees them; contact details are checked and limited to 5 requests per email address and per phone number a day. Agent-to-agent (A2A): agent card at https://www.feminiqrides.nl/.well-known/agent-card.json, JSON-RPC at https://www.feminiqrides.nl/a2a."
  },
  "servers": [
    {
      "url": "https://www.feminiqrides.nl"
    }
  ],
  "paths": {
    "/api/quote": {
      "post": {
        "operationId": "quote",
        "summary": "Check rules and price. No booking, email or payment is created; no personal details or terms acceptance required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "fixed": {
                  "summary": "Schiphol transfer with a return trip, rules/price check only",
                  "value": {
                    "car": "bmw5",
                    "date": "2026-10-08",
                    "time": "10:00",
                    "passengers": 2,
                    "type": "fixed",
                    "routeId": "ams-spl",
                    "direction": "to",
                    "address": "Dam 1, 1012 JS Amsterdam",
                    "returnTrip": {
                      "date": "2026-10-15",
                      "time": "18:30",
                      "flight": "KL 1002"
                    }
                  }
                },
                "custom": {
                  "summary": "Custom ride with a return trip the same afternoon, rules/price check only",
                  "value": {
                    "car": "bmw5",
                    "date": "2026-10-08",
                    "time": "10:00",
                    "passengers": 2,
                    "type": "custom",
                    "pickup": "Dam 1, 1012 JS Amsterdam",
                    "dropoff": "Stationsplein 9, 1012 AB Amsterdam",
                    "returnTrip": {
                      "date": "2026-10-08",
                      "time": "16:00"
                    }
                  }
                },
                "tour": {
                  "summary": "Private tour with agreed pickup, paid in full in advance, rules/price check only",
                  "value": {
                    "car": "bmw5",
                    "date": "2026-10-08",
                    "time": "10:00",
                    "passengers": 2,
                    "type": "tour",
                    "tourIds": [
                      "amsterdam-city-tour"
                    ],
                    "pickup": "Hotel Okura, Ferdinand Bolstraat 333, Amsterdam",
                    "payment": "full"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Eligible under the pricing/time rules (not a guarantee of driver availability).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Not eligible; error is a machine-readable reason.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited."
          },
          "502": {
            "description": "Custom-ride fare could not be calculated; retry later."
          },
          "503": {
            "description": "quote_unavailable: signing service not configured; no accepted quote is returned."
          }
        }
      }
    },
    "/api/book": {
      "post": {
        "operationId": "book",
        "summary": "Create a booking request for any ride type. Has side effects: stores the request and emails the driver and the customer. No payment is created here; the payment request follows by email after Feminiq Rides confirms the ride.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request received (paymentRequired is always false). Feminiq Rides confirms availability and the price, then emails a payment request (SumUp) for the deposit or the full price. emailConfirmationRequired=true (requests sent without the website, e.g. by an AI agent or API client): the request is not sent to Feminiq Rides yet; the customer gets an email with a Confirm link valid for 24 hours and she is told only after that click. A ride sent with joinGroup pointing at a request that still waits for its click is added to it: one click confirms them together.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation or rule error (see error codes). Contact checks: phone (not a real phone number: 8 to 15 digits), email_domain (the email domain cannot receive mail), email_disposable (throwaway inbox services are refused).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "origin: Origin not allowed. blocked: these contact details were blocked by the owner; the customer can use WhatsApp."
          },
          "409": {
            "description": "quote_required, quote_invalid, quote_expired, quote_changed or quote_mismatch: reviewRequired=true. No booking or mail is created. Obtain a new quote and renewed explicit customer approval, then retry with its token. booking_conflict (no reviewRequired): a request with this bookingId (or quote and email) already exists with different details; the body carries its bookingId. Do not quote again for it: change or cancel the existing request, or use a new bookingId for a separate ride.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limit: too many requests from this address (5 per 15 minutes). rate_limit_contact: too many requests with this email address or phone number (5 per 24 hours)."
          },
          "502": {
            "description": "request_failed: the request could not be stored or emailed. Retrying the identical request with the same bookingId is safe; after editing details use a new bookingId."
          },
          "503": {
            "description": "payment_unavailable or mail_unavailable: service not configured. agent_capacity: today's ceiling for requests sent without the website was reached; the customer can request the ride on the website or via WhatsApp."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "QuoteRequest": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/RideRequirements"
          }
        ],
        "required": [
          "type",
          "car",
          "date",
          "time",
          "passengers"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "fixed",
              "custom",
              "tour"
            ]
          },
          "car": {
            "type": "string",
            "enum": [
              "bmw5",
              "vclass"
            ],
            "description": "BMW 5 Series Touring (bmw5) max 4 passengers, Mercedes V-Class (vclass) max 7 passengers."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Local date in Europe/Amsterdam."
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "description": "Local time in Europe/Amsterdam, between 04:00 and 20:00."
          },
          "passengers": {
            "type": "integer",
            "minimum": 1,
            "maximum": 7
          },
          "luggageLarge": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8,
            "default": 0,
            "description": "Large check-in suitcases. The BMW takes at most 3; more needs the V-Class (error code luggage)."
          },
          "luggageSmall": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8,
            "default": 0,
            "description": "Pieces of hand luggage. The BMW takes at most 3; more needs the V-Class (error code luggage)."
          },
          "returnTrip": {
            "type": "object",
            "description": "Airport transfers (fixed) and custom rides only; tours fail with retour_type, and a value that is not an object fails with retour. Books the return leg as well, with the same vehicle, passengers and luggage: on a fixed route the opposite direction between the same address and Schiphol, priced as a second ride at the route price; on a custom ride from the drop-off back to the pickup, where the fare estimate covers both legs and is split evenly over them. The return must be at least one hour after the first ride (retour_order), between 04:00 and 20:00 (retour_hours), not on a blocked date (retour_date_blocked) and at most 2 years ahead (date_range); missing or invalid values fail with retour_date, retour_time or retour_datetime. There is no name card on either leg (meet_greet_unavailable). The request is stored as two linked bookings, bookingId and `<bookingId>-ret` (so bookingId may have at most 51 characters when a returnTrip is sent), confirmed together and paid with one payment request: 25% of the combined price of the legs, or all of it with payment=full.",
            "properties": {
              "date": {
                "type": "string",
                "format": "date",
                "description": "Local date of the return ride in Europe/Amsterdam."
              },
              "time": {
                "type": "string",
                "pattern": "^\\d{2}:\\d{2}$",
                "description": "Local pickup time of the return ride in Europe/Amsterdam, between 04:00 and 20:00, at least 1 hour after the first ride."
              },
              "flight": {
                "type": "string",
                "maxLength": 24,
                "description": "Optional flight number for the return ride."
              }
            },
            "required": [
              "date",
              "time"
            ]
          },
          "routeId": {
            "type": "string",
            "description": "Fixed routes only; ids from /data/rates.json. The server reprices to the route the address belongs to.",
            "enum": [
              "ams-spl",
              "noord-spl",
              "zaandam-spl",
              "aveen-spl",
              "dh-spl",
              "rtm-spl",
              "almere-spl",
              "almere-buiten-spl",
              "utrecht-spl",
              "eindhoven-spl"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "to",
              "from"
            ],
            "description": "Fixed routes only: to or from Schiphol."
          },
          "address": {
            "type": "string",
            "minLength": 4,
            "maxLength": 180,
            "description": "Fixed routes only: the non-airport pickup/drop-off address. The fare follows the route zone of the address (postcode first, then town name; a hotel or town named without a postcode is placed by a server-side lookup, and the selected route applies if that lookup fails). An address whose Dutch postcode is in no fixed-route zone is refused with error code address_zone; book it as a single ride."
          },
          "payment": {
            "type": "string",
            "enum": [
              "deposit",
              "full"
            ],
            "default": "deposit",
            "description": "How the customer pays once the ride is confirmed: deposit = a payment request for 25% of the price, rest on the day of the ride; full = a payment request for the full price. With a returnTrip both legs are confirmed together and paid with ONE payment request on these terms (the sum over the legs)."
          },
          "pickup": {
            "type": "string",
            "description": "Required for custom rides and tours: agreed pickup address or hotel, preserved in review and confirmations.",
            "minLength": 4,
            "maxLength": 180
          },
          "dropoff": {
            "type": "string",
            "description": "Required for custom rides: drop-off address.",
            "minLength": 4,
            "maxLength": 180
          },
          "tourIds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "amsterdam-city-tour",
                "countryside-tour",
                "keukenhof-tour",
                "aalsmeer-flower-auction",
                "dutch-water-defence-line",
                "giethoorn-day-trip",
                "south-holland-tour",
                "rotterdam-kinderdijk",
                "kroller-muller",
                "efteling-day-trip",
                "bruges-day-trip",
                "roermond-outlet",
                "mall-of-the-netherlands",
                "batavia-stad",
                "style-outlet-amsterdam"
              ]
            },
            "minItems": 1,
            "maxItems": 5,
            "description": "Tours only; ids from /data/tours.json.",
            "uniqueItems": true
          },
          "ticketService": {
            "type": "boolean",
            "description": "Tours only: attraction ticket purchase assistance, requires 28 days' notice."
          },
          "driverName": {
            "type": "string",
            "maxLength": 60,
            "description": "Optional. Name of a preferred driver, printed as \"Driver: ...\" in the customer's confirmation and calendar file. Leave it out unless the customer asks for a specific driver; it is not the passenger's name."
          },
          "flight": {
            "type": "string",
            "maxLength": 24
          },
          "notes": {
            "type": "string",
            "maxLength": 1000
          },
          "lang": {
            "type": "string",
            "enum": [
              "en",
              "nl",
              "de",
              "fr",
              "es",
              "ar",
              "zh-CN"
            ],
            "description": "Language of the emails the customer receives (the Confirm link, status and payment mails) and of the quote card. Use the customer's own language; default en."
          }
        }
      },
      "BookRequest": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/RideRequirements"
          }
        ],
        "required": [
          "type",
          "car",
          "date",
          "time",
          "passengers",
          "name",
          "email",
          "phone",
          "acceptedTerms",
          "quoteToken"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "fixed",
              "custom",
              "tour"
            ]
          },
          "car": {
            "type": "string",
            "enum": [
              "bmw5",
              "vclass"
            ],
            "description": "BMW 5 Series Touring (bmw5) max 4 passengers, Mercedes V-Class (vclass) max 7 passengers."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Local date in Europe/Amsterdam."
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "description": "Local time in Europe/Amsterdam, between 04:00 and 20:00."
          },
          "passengers": {
            "type": "integer",
            "minimum": 1,
            "maximum": 7
          },
          "luggageLarge": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8,
            "default": 0,
            "description": "Large check-in suitcases. The BMW takes at most 3; more needs the V-Class (error code luggage)."
          },
          "luggageSmall": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8,
            "default": 0,
            "description": "Pieces of hand luggage. The BMW takes at most 3; more needs the V-Class (error code luggage)."
          },
          "returnTrip": {
            "type": "object",
            "description": "Airport transfers (fixed) and custom rides only; tours fail with retour_type, and a value that is not an object fails with retour. Books the return leg as well, with the same vehicle, passengers and luggage: on a fixed route the opposite direction between the same address and Schiphol, priced as a second ride at the route price; on a custom ride from the drop-off back to the pickup, where the fare estimate covers both legs and is split evenly over them. The return must be at least one hour after the first ride (retour_order), between 04:00 and 20:00 (retour_hours), not on a blocked date (retour_date_blocked) and at most 2 years ahead (date_range); missing or invalid values fail with retour_date, retour_time or retour_datetime. There is no name card on either leg (meet_greet_unavailable). The request is stored as two linked bookings, bookingId and `<bookingId>-ret` (so bookingId may have at most 51 characters when a returnTrip is sent), confirmed together and paid with one payment request: 25% of the combined price of the legs, or all of it with payment=full.",
            "properties": {
              "date": {
                "type": "string",
                "format": "date",
                "description": "Local date of the return ride in Europe/Amsterdam."
              },
              "time": {
                "type": "string",
                "pattern": "^\\d{2}:\\d{2}$",
                "description": "Local pickup time of the return ride in Europe/Amsterdam, between 04:00 and 20:00, at least 1 hour after the first ride."
              },
              "flight": {
                "type": "string",
                "maxLength": 24,
                "description": "Optional flight number for the return ride."
              }
            },
            "required": [
              "date",
              "time"
            ]
          },
          "routeId": {
            "type": "string",
            "description": "Fixed routes only; ids from /data/rates.json. The server reprices to the route the address belongs to.",
            "enum": [
              "ams-spl",
              "noord-spl",
              "zaandam-spl",
              "aveen-spl",
              "dh-spl",
              "rtm-spl",
              "almere-spl",
              "almere-buiten-spl",
              "utrecht-spl",
              "eindhoven-spl"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "to",
              "from"
            ],
            "description": "Fixed routes only: to or from Schiphol."
          },
          "address": {
            "type": "string",
            "minLength": 4,
            "maxLength": 180,
            "description": "Fixed routes only: the non-airport pickup/drop-off address. The fare follows the route zone of the address (postcode first, then town name; a hotel or town named without a postcode is placed by a server-side lookup, and the selected route applies if that lookup fails). An address whose Dutch postcode is in no fixed-route zone is refused with error code address_zone; book it as a single ride."
          },
          "payment": {
            "type": "string",
            "enum": [
              "deposit",
              "full"
            ],
            "default": "deposit",
            "description": "How the customer pays once the ride is confirmed: deposit = a payment request for 25% of the price, rest on the day of the ride; full = a payment request for the full price. With a returnTrip both legs are confirmed together and paid with ONE payment request on these terms (the sum over the legs)."
          },
          "pickup": {
            "type": "string",
            "description": "Required for custom rides and tours: agreed pickup address or hotel, preserved in review and confirmations.",
            "minLength": 4,
            "maxLength": 180
          },
          "dropoff": {
            "type": "string",
            "description": "Required for custom rides: drop-off address.",
            "minLength": 4,
            "maxLength": 180
          },
          "tourIds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "amsterdam-city-tour",
                "countryside-tour",
                "keukenhof-tour",
                "aalsmeer-flower-auction",
                "dutch-water-defence-line",
                "giethoorn-day-trip",
                "south-holland-tour",
                "rotterdam-kinderdijk",
                "kroller-muller",
                "efteling-day-trip",
                "bruges-day-trip",
                "roermond-outlet",
                "mall-of-the-netherlands",
                "batavia-stad",
                "style-outlet-amsterdam"
              ]
            },
            "minItems": 1,
            "maxItems": 5,
            "description": "Tours only; ids from /data/tours.json.",
            "uniqueItems": true
          },
          "ticketService": {
            "type": "boolean",
            "description": "Tours only: attraction ticket purchase assistance, requires 28 days' notice."
          },
          "driverName": {
            "type": "string",
            "maxLength": 60,
            "description": "Optional. Name of a preferred driver, printed as \"Driver: ...\" in the customer's confirmation and calendar file. Leave it out unless the customer asks for a specific driver; it is not the passenger's name."
          },
          "flight": {
            "type": "string",
            "maxLength": 24
          },
          "notes": {
            "type": "string",
            "maxLength": 1000
          },
          "lang": {
            "type": "string",
            "enum": [
              "en",
              "nl",
              "de",
              "fr",
              "es",
              "ar",
              "zh-CN"
            ],
            "description": "Language of the emails the customer receives (the Confirm link, status and payment mails) and of the quote card. Use the customer's own language; default en."
          },
          "bookingId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{8,55}$",
            "description": "Optional. Client-generated id, 8 to 55 characters (51 with a returnTrip); a longer id is refused with 400 error `booking_id`. Re-sending the identical request with the same id is idempotent and a changed request needs a new id. When left out, the server derives a stable id (api-…) from the quoteToken and email, so retries never book twice."
          },
          "quoteToken": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Signed token returned by POST /api/quote. Send unchanged only after the customer approves that quote's total and payment. It binds the normalized journey, vehicle, passengers, payment choice, return trip, options, fare, rules version and 15-minute expiry. HTTP 409 reviewRequired=true means obtain a new quote and renewed customer approval before retrying. Never supply your own price."
          },
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 100
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "minLength": 7,
            "maxLength": 30
          },
          "company": {
            "type": "string",
            "description": "Honeypot for the website's hidden field. Must be omitted or empty; a non-empty value is treated as automated traffic and the request is acknowledged with suppressed:true without creating a booking or email."
          },
          "acceptedTerms": {
            "type": "boolean",
            "const": true,
            "description": "Must be an explicit act by the customer after reading the Terms & Conditions. Agents must not set this on a customer's behalf without their consent."
          },
          "joinGroup": {
            "type": "object",
            "description": "Optional. Adds this ride to the request group of an earlier ride, so the owner approves them together and the customer receives ONE payment request (the sum of each ride's 25%, or full price where chosen). Use the `groupId` and `groupToken` returned for the earlier request. It is ignored (the ride stands alone) when the token or email does not match, or once any ride of the group has been approved.",
            "properties": {
              "id": {
                "type": "string"
              },
              "token": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "token"
            ]
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "required": [
          "ok",
          "eligible",
          "quoteToken",
          "rulesVersion",
          "quotedAt",
          "validUntil",
          "quote"
        ],
        "properties": {
          "quoteToken": {
            "type": "string",
            "description": "Pass to POST /api/book after explicit customer review and approval. Not a booking or availability guarantee."
          },
          "ok": {
            "type": "boolean"
          },
          "eligible": {
            "type": "boolean"
          },
          "availability": {
            "const": "not_guaranteed"
          },
          "rulesVersion": {
            "type": "string"
          },
          "quotedAt": {
            "type": "string",
            "format": "date-time"
          },
          "validUntil": {
            "type": "string",
            "format": "date-time"
          },
          "timezone": {
            "const": "Europe/Amsterdam"
          },
          "currency": {
            "const": "EUR"
          },
          "vatIncluded": {
            "type": "boolean"
          },
          "quote": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "car": {
                "type": "string"
              },
              "routeId": {
                "type": "string"
              },
              "routeLabel": {
                "type": "string"
              },
              "tourIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "basePrice": {
                "type": "number"
              },
              "totalPrice": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "depositCents": {
                "type": "integer",
                "description": "Amount of the payment request sent after confirmation: 25% of totalPrice, or all of it when payment=full. With a return trip the one payment request covers both legs (the sum of each leg's 25%, or of the full prices), so it can differ from this figure by a cent or after the owner adjusts a leg's price."
              },
              "payOnline": {
                "type": "string",
                "description": "payment_request_after_confirmation: nothing is charged until Feminiq Rides confirms the ride."
              },
              "payInVehicle": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Remainder paid on the day of the ride."
              },
              "pickup": {
                "type": "string"
              },
              "dropoff": {
                "type": "string"
              },
              "address": {
                "type": "string"
              },
              "direction": {
                "type": "string"
              },
              "ticketService": {
                "type": "boolean"
              },
              "passengers": {
                "type": "integer"
              },
              "payment": {
                "type": "string",
                "enum": [
                  "deposit",
                  "full"
                ]
              },
              "outboundPrice": {
                "type": "number",
                "description": "Return trips only: the price of the first ride; totalPrice covers both legs."
              },
              "returnTrip": {
                "type": "object",
                "description": "Return trips only: the return leg. totalPrice is its price; direction is set for fixed routes only.",
                "properties": {
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "time": {
                    "type": "string"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "to",
                      "from"
                    ]
                  },
                  "totalPrice": {
                    "type": "number"
                  }
                },
                "required": [
                  "date",
                  "time",
                  "totalPrice"
                ]
              }
            }
          }
        }
      },
      "BookResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "paymentRequired": {
            "type": "boolean",
            "const": false,
            "description": "Always false: payment is requested by email after Feminiq Rides confirms the ride."
          },
          "bookingId": {
            "type": "string"
          },
          "suppressed": {
            "type": "boolean"
          },
          "groupId": {
            "type": "string",
            "description": "The request group this ride belongs to (the first ride's booking id)."
          },
          "groupToken": {
            "type": "string",
            "description": "Proof of having made the group's first request; send it as joinGroup.token with the next ride."
          },
          "grouped": {
            "type": "boolean",
            "description": "True when this request is part of a group of more than one booking (a return trip, or a ride that joined an earlier request)."
          },
          "emailConfirmationRequired": {
            "type": "boolean",
            "description": "True when the request waits for the customer to click the Confirm link emailed to them (requests without the website's Origin header). Nothing reaches Feminiq Rides before that click; unconfirmed requests are deleted after two days. groupId and groupToken are already valid for joinGroup."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "reviewRequired": {
            "type": "boolean",
            "description": "On HTTP 409: no booking is created. Get a new quote and ask the customer to approve its total and payment again."
          },
          "error": {
            "type": "string"
          },
          "bookingId": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "description": "error is one of: address, address_zone, agent_capacity, blocked, body, body_too_large, booking_conflict, booking_id, bundle, capacity, car, content_type, custom_estimate_failed, date, date_blocked, date_range, datetime, direction, driver_name, driver_phone, dropoff, email, email_disposable, email_domain, flight, hours, internal, lead_time, luggage, mail_unavailable, meet_greet_unavailable, method, name, notes, origin, payment, payment_unavailable, phone, pickup, quote_changed, quote_expired, quote_invalid, quote_mismatch, quote_required, quote_unavailable, rate_limit, rate_limit_contact, request_failed, retour, retour_date, retour_date_blocked, retour_datetime, retour_hours, retour_order, retour_time, retour_type, route, terms, ticket_lead_time, time, tour, tour_duplicate, tour_limit, tour_price, tour_season, type."
      },
      "RideRequirements": {
        "type": "object",
        "properties": {
          "pickup": {
            "type": "string",
            "minLength": 4,
            "maxLength": 180,
            "description": "Required for custom rides and tours: agreed pickup address or hotel, preserved in review and confirmations."
          },
          "dropoff": {
            "type": "string",
            "minLength": 4,
            "maxLength": 180
          },
          "tourIds": {
            "type": "array",
            "uniqueItems": true
          },
          "email": {
            "type": "string",
            "maxLength": 254
          },
          "phone": {
            "type": "string",
            "pattern": "^[+()0-9 .-]{7,30}$"
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "fixed"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "required": [
                "routeId",
                "direction",
                "address"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "custom"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "required": [
                "pickup",
                "dropoff"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "tour"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "required": [
                "tourIds",
                "pickup"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "car": {
                  "const": "bmw5"
                }
              },
              "required": [
                "car"
              ]
            },
            "then": {
              "properties": {
                "passengers": {
                  "maximum": 4
                },
                "luggageLarge": {
                  "maximum": 3
                },
                "luggageSmall": {
                  "maximum": 3
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "tour"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "not": {
                "required": [
                  "returnTrip"
                ]
              }
            }
          }
        ]
      }
    }
  }
}
